通过查看uboot源码顶层目录下的链接脚本u-boot.lds:
- OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
- OUTPUT_ARCH(arm)
- ENTRY(_start)
- SECTIONS
- {
- . = 0x00000000;
- . = ALIGN(4);
- .text :
- {
- cpu/arm920t/start.o (.text)
- *(.text)
- }
- ......
可以从中知道程序的入口点是_start,定位于cpu/arm920t/start.S(即u-boot 启动的第一阶段)。下面本人谈谈对start.S中对.balignl和魔数0xdeadbeef的理解。
uboot入口_start: - .globl _start // 不占用内存
- _start: b start_code //每条指令占用4字节 异常向量表
- ldr pc, _undefined_instruction
- ldr pc, _software_interrupt
- ldr pc, _prefetch_abort
- ldr pc, _data_abort
- ldr pc, _not_used
- ldr pc, _irq
- ldr pc, _fiq
- _undefined_instruction: .word undefined_instruction
- _software_interrupt: .word software_interrupt
- _prefetch_abort: .word prefetch_abort
- _data_abort: .word data_abort
- _not_used: .word not_used
- _irq: .word irq
- _fiq: .word fiq
- .balignl 16,0xdeadbeef //16字节对齐,不对齐的地址以0xdeadbeef填充
.balignl是条伪指令,用于字节对齐的。对于S3C2440来说,它是32位处理器,要求指令必须4字节对齐。与.balignl相似的伪指令如下表所示: 四种功能基本相同,不同之处在于填充时的字节数。.align和.balign是1个字节1个字节的填充(区别在于对齐方式不用),.balignw是2个字节2个字节的填充,而.balignl一次填充4个字节。我们以balignl为例说明,它的完整指令格式为: .balignl {alignment} {,fill} {,max}。 第一个参数alignment为一个正整数,对齐时要以alignment的值的整数倍为结束地址(补充:这段描述摘录于别人博客,经过自己验证之后发现.align与其他三个以b开头的伪指令不同是以2^alignment的整数倍为地址,后面会加以讲解),以当前地址为起始地址,进行字节填充,比如当前地址为20,而alignment的值我们设定为16,那么字节填充自20开始,结束于20后第一个16的倍数地址处,即32处。 第二个参数fill即我们选定的,用来填充的数值。balignl模式下,最大为4字节,不够4字节系统会自动补够4字节,此参数可选,不标则采用默认值0。 第三个参数max也是可选项,默认值为alignment。若对齐时偏移量大于max,则不偏移。同上例,从16--32,偏移量为16,如果max我们设置为8,那么该偏移不进行。 为了更容易理解,截取start.S最开始一部分代码做个试验,对它单独编译。文件名为test.S,代码如下:
- .globl _start
- _start: b start_code
- ldr pc, _undefined_instruction
- ldr pc, _software_interrupt
- ldr pc, _prefetch_abort
- ldr pc, _data_abort
- ldr pc, _not_used
- ldr pc, _irq
- @ldr pc, _fiq
- _undefined_instruction: .word undefined_instruction
- _software_interrupt: .word software_interrupt
- _prefetch_abort: .word prefetch_abort
- _data_abort: .word data_abort
- _not_used: .word not_used
- _irq: .word irq
- @_fiq: .word fiq
- .balignl 16,0xdeadbeef
-
- start_code:
- /*
- * set the cpu to SVC32 mode
- */
- mrs r0, cpsr
- bic r0, r0, #0x1f
- orr r0, r0, #0xd3
- msr cpsr, r0
使用交叉编译器编译后,查看反汇编代码如下:
[samba@localhost ~]$ arm-linux-as test.S -o test.o test.S: Assembler messages: test.S:0: Warning: end of file not at end of a line; newline inserted [samba@localhost ~]$ arm-linux-objdump -d test.o
test.o: file format elf32-littlearm
Disassembly of section .text:
00000000 <_start>: 0: ea00000e b 40 <start_code> 4: e59ff014 ldr pc, [pc, #20] ; 20 <_undefined_instruction> 8: e59ff014 ldr pc, [pc, #20] ; 24 <_software_interrupt> c: e59ff014 ldr pc, [pc, #20] ; 28 <_prefetch_abort> 10: e59ff014 ldr pc, [pc, #20] ; 2c <_data_abort> 14: e59ff014 ldr pc, [pc, #20] ; 30 <_not_used> 18: e59ff014 ldr pc, [pc, #20] ; 34 <_irq> 1c: e59ff014 ldr pc, [pc, #20] ; 38 <_fiq>
00000020 <_undefined_instruction>: 20: 00000000 .word 0x00000000 00000024 <_software_interrupt>: 24: 00000000 .word 0x00000000 00000028 <_prefetch_abort>: 28: 00000000 .word 0x00000000 0000002c <_data_abort>: 2c: 00000000 .word 0x00000000 00000030 <_not_used>: 30: 00000000 .word 0x00000000 00000034 <_irq>: 34: 00000000 .word 0x00000000 00000030 <_irq>: 30: 00000000 .word 0x00000000 34: deadbeef .word 0xdeadbeef 38: deadbeef .word 0xdeadbeef 3c: deadbeef .word 0xdeadbeef 00000040 <start_code>: 40: e10f0000 mrs r0, CPSR 44: e3c0001f bic r0, r0, #31 48: e38000d3 orr r0, r0, #211 ; 0xd3 4c: e129f000 msr CPSR_fc, r0
可以看到由于地址34~3c不是16字节对齐,所以用0xdeadbeef填充。那么我们修改test.S再做一个试验,屏蔽掉.balignl 16,0xdeadbeef,在它之后添加.byte 0x11,使用交叉编译器编译,结果如下: [samba@localhost ~]$ arm-linux-as test.S -o test.o test.S: Assembler messages: test.S:0: Warning: end of file not at end of a line; newline inserted test.S:2: Error: misaligned branch destination 提示边界对齐出错,这说明arm编译器没有自动帮我们对齐。添加的 .byte 0x11 只占用了1字节,导致其后的全部指令地址都没有对齐,而arm的指令要求32位对齐,不然无法寻址。所以在第二行跳转去start_code时出现了问题,start_code此时的存储地址不是4字节的整数倍,当然无法寻址了。 要解决这个问题,这里我们就需要手动对齐了。在59行后添加如下代码: .align 4,0x88 保存后再编译就顺利通过了。为了能更清楚知道它的作用,我们来看看反汇编代码: [samba@localhost ~]$ arm-linux-as test.S -o test.o test.S: Assembler messages: test.S:0: Warning: end of file not at end of a line; newline inserted [samba@localhost ~]$ arm-linux-objdump -d test.o
test.o: file format elf32-littlearm
Disassembly of section .text:
00000000 <_start>: 0: ea00000e b 40 <start_code> 4: e59ff010 ldr pc, [pc, #16] ; 1c <_undefined_instruction> 8: e59ff010 ldr pc, [pc, #16] ; 20 <_software_interrupt> c: e59ff010 ldr pc, [pc, #16] ; 24 <_prefetch_abort> 10: e59ff010 ldr pc, [pc, #16] ; 28 <_data_abort> 14: e59ff010 ldr pc, [pc, #16] ; 2c <_not_used> 18: e59ff010 ldr pc, [pc, #16] ; 30 <_irq>
0000001c <_undefined_instruction>: 1c: 00000000 .word 0x00000000 00000020 <_software_interrupt>:
20: 00000000 .word 0x00000000 00000024 <_prefetch_abort>: 24: 00000000 .word 0x00000000 00000028 <_data_abort>: 28: 00000000 .word 0x00000000 0000002c <_not_used>: 2c: 00000000 .word 0x00000000 00000030 <_irq>: 30: 00000000 .word 0x00000000 34: 88888811 .word 0x88888811 38: 88888888 .word 0x88888888 3c: 88888888 .word 0x88888888
00000040 <start_code>: 40: e10f0000 mrs r0, CPSR 44: e3c0001f bic r0, r0, #31 48: e38000d3 orr r0, r0, #211 ; 0xd3 4c: e129f000 msr CPSR_fc, r0
结果发现不是我们想要的4字节对齐,而是16字节对齐,为什么呢?经过查找资料发现,.align是以2^alignment方式对齐,在这里就是2^4=16字节对齐方式。 说到这里,我们已经理解了字节对齐伪指令的含义了,紧接着的疑问是,经过之前的两次反汇编我们发现地址已经是4字节对齐了,符合32为处理器地址4字节对齐的要求了。u-boot为什么要加.balignl 16,0xdeadbeef这句话呢?0xdeadbeef是一个魔数,本身数值没有什么意义,也可以设为其他值,就是一个标识作用的信息。把它放置在异常向量之后,用它来标识程序真正开始的地方(自己的理解)。
|