题记:
解决了两种情景下ELF文件转化为binary格式,固化到flash中启动的问题,对链接器和加载器原理又有了一个更为深入的认识。结合该问题,对AT91RM9200的官方loader和boot做了些修正。
在嵌入式系统中,固化到非易失性存储介质中的软件是binary格式。Linux下生成的可执行文件是ELF格式,不能直接固化,必须转化为bin文件。就处理方法而言,视链接方式不同,可以有两种不同的方法,下面分别举例说明。
(一)以at91rm9200 boot官方程序为例,介绍方法一
boot最终得到boot.bin,为bin文件,可以直接固化到NVM中。它有两个组成部分:boot.text和boot.data。处理方式如下:
|
all: boot.bin
boot.bin: boot.text boot.data cat $^ > $@
boot.text: boot $(OBJCOPY) -O binary -j .text $< $@
boot.data: boot $(OBJCOPY) -O binary -j .data $< $@
boot: $(OBJ) $(LD) $(LDFLAGS) $^ -o $@
|
下面看一下链接器的设置。
|
[armlinux@lqm boot]$ cat ld.script MEMORY { ram : ORIGIN = 0x20000000, LENGTH = 0xf000 rom : ORIGIN = 0x00000000, LENGTH = 0xf000 }
SECTIONS { .text : { _stext = . ; *(.text) *(.rodata*) . = ALIGN(4); _etext = . ; } > rom .data : { _sdata = . ; *(.data) *(.glue_7*) . = ALIGN(4); _edata = . ; } > ram .bss : { _sbss = . ; *(.bss) . = ALIGN(4); _ebss = . ; } > ram }
|
可见,就内存空间的安排而言,.text段放在rom里,而.data放在ram里。具体分布如下:
---------------- 0x0000 0000
.text
----------------
...
---------------- 0x2000 0000
.data
---------------- (0x2000 0000 + sizeof(.data))
.bss
----------------
也就是说,.text和.data段的地址是不连续的,.data段和.bss则是连续的。而ELF具有可执行属性的段只有.text和.data。
objcopy如果直接采用-O
binary选项,实际上是解析ELF文件信息,然后复制.text段首地址到.data的段尾地址。很明显,像地址不连续的情况,就会出现空洞。如
boot,空洞会相当大,根本不适合下载。下面根据Linux下工具进行具体分析。
|
[armlinux@lqm boot]$ arm-linux-readelf -a boot > elfinfo.txt
|
这样可以得到生成的ELF文件的所有信息。主要信息如下:
|
Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .text PROGBITS 00000000 000074 002744 00 AX 0 0 4 [ 2] .data PROGBITS 20000000 0027b8 0001c4 00 WAX 0 0 4 [ 3] .bss NOBITS 200001c4 00297c 00841c 00 WA 0 0 4 [ 4] .comment PROGBITS 00000000 00297c 000098 00 0 0 1 [ 5] .shstrtab STRTAB 00000000 002a14 000035 00 0 0 1 [ 6] .symtab SYMTAB 00000000 002b8c 000710 10 7 53 4 [ 7] .strtab STRTAB 00000000 00329c 0003e0 00 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings) I (info), L (link order), G (group), x (unknown) O (extra OS processing required) o (OS specific), p (processor specific)
Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x000074 0x00000000 0x00000000 0x02744 0x02744 R E 0x4 LOAD 0x0027b8 0x20000000 0x20000000 0x001c4 0x085e0 RWE 0x4
Section to Segment mapping: Segment Sections... 00 .text 01 .data .bss
|
可以看出,.text从0x0000 0000开始,大小为0x2744Bytes,.data从0x2000
0000开始,大小为0x1c4.生成的bin文件应该为(0x2744+0x01c4)=0x2908=10504 Bytes。这个可以用ls
-l来验证。
不过,采用这种方式,需要在程序中进行处理,主要任务就是完成代码的搬移和bss的清0处理。这也就是crt0.S的作用。
|
[armlinux@lqm boot]$ cat crt0.S @ r0 -> start of flash @ r1 -> where to load data @ r2 -> start of program
.text .align .global main,_main
main: _main: # copy .data section ldr r3, =_etext ldr r4, =_sdata ldr r5, =_edata subs r5, r5, r4 bl copydata
# clear .bss section ldr r4, =_sbss ldr r5, =_ebss subs r5, r5, r4 mov r0, #0 bl clearbss
# and jump to the kernel b boot
copydata: subs r5, r5, #4 ldr r6, [r3], #4 str r6, [r4], #4 bne copydata mov pc, lr
clearbss: subs r5, r5, #4 str r0, [r3], #4 bne clearbss mov pc, lr
|
代码比较简单,不做分析。
(二)以自修改的at91rm9200 loader为例,介绍方法二
|
all: loader.bin
loader.bin: $(OBJ) $(LD) $(LDFLAGS) $^ -o loader $(OBJCOPY) -O binary loader $@
|
链接设置如下:
|
[armlinux@lqm loader]$ cat ld.script MEMORY { ram : ORIGIN = 0x200000, LENGTH = 0x3000 }
SECTIONS { .text : { _stext = . ; *(.text) *(.rodata) . = ALIGN(4); _etext = . ; } > ram .data : { _sdata = . ; *(.data) *(.glue_7*) . = ALIGN(4); _edata = . ; } > ram .bss : { _sbss = . ; *(.bss) . = ALIGN(4); _ebss = . ; } > ram }
|
可见.text和.data的地址是连续的。这样,原来的loader代码中文件crt0.S是没有必要的。
boot如果采用方法二的处理方法,生成的bin文件为0x2000 01c4,可以很容易验证。
理解了这些,在自己设计boot
loader时,就会综合考虑链接器设置的内存空间分配和bin生成方式选择。显然,方法一适合.text和.data地址不连续的情况,方法二适合.
text和.data地址连续的情况。分析清楚了这些,也解决了原来在s3c2410设计实验程序时出现的生成bin文件过大的问题了。
解决这个问题后,对官方的loader和boot程序做了一些修正,其实因为对U-boot做了启动位置无关性的改进后,boot程序没有什么必要了,但还是备份一下,算是对工作的一个总结吧。
 |
| 文件: |
myloader.tar.gz |
| 大小: |
57KB |
| 下载: |
下载 | |
 |
| 文件: |
myboot.tar.gz |
| 大小: |
51KB |
| 下载: |
下载 | |
至此,AT91RM9200 kernel之前的引导加载程序就比较完善和稳定了。