好了,言归正传,先将部分代码贴出,来自于uboot-2011.06目录下arch\arm\cpu\arm920t\start.S。
- /*
- * armboot - Startup Code for ARM920 CPU-core
- *
- * Copyright (c) 2001 Marius Gr鰃er
- * Copyright (c) 2002 Alex Z黳ke
- * Copyright (c) 2002 Gary Jennejohn
- *
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- */
- #include
- #include
- #include
- /*
- *************************************************************************
- *
- * Jump vector table as in table 3.1 in [1]
- *
- *************************************************************************
- */
- .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
问题出在58行,.balignl 16,0xdeadbeef,查了以前的笔记得知这是一个伪指令,主要是字节对齐用的。对于某些处理器来说,所编写的代码不对齐并不会报错,但编译器为了优化,也可能会自动帮你对齐。但对于另外一些处理器的编译器来说,汇编代码里必须手动保持对齐,否则编译器会报错,arm处理器就是如此。本着精读的态度,上网查了一下资料,结果花掉了1下午的时间o(︶︿︶)o 。。。
balignl有几个“近亲”,具体看下面截图:
四种功能基本相同,不同之处在于填充时的字节数。.align和.balign是1个字节1个字节的填充,.balignw是2个字节2个字节的填充,而.balignl一次填充4个字节。
我们以balignl为例说明,它的完整指令格式为: .balignl {alignment} {,fill} {,max}。
第一个参数alignment为一个正整数,对齐时要以alignment的值的整数倍为结束地址,以当前地址为起始地址,进行字节填充,比如当前地址为20,而alignment的值我们设定为16,那么字节填充自20开始,结束于20后第一个16的倍数地址处,即32处。
第二个参数fill即我们选定的,用来填充的数值。balignl模式下,最大为4字节,不够4字节系统会自动补够4字节,此参数可选,不标则采用默认值0。
第三个参数max也是可选项,默认值为alignment。若对齐时偏移量大于max,则不偏移。同上例,从16--32,偏移量为16,如果max我们设置为8,那么该偏移不进行。
为了能更容易理解,我们实际操作下。
将start.S拷贝至任意目录,保留58行前以及start_code段的代码(大约在117~124行),方便起见,我直接贴出裁剪后的代码:
- /*
- * armboot - Startup Code for ARM920 CPU-core
- *
- * Copyright (c) 2001 Marius Gr鰃er
- * Copyright (c) 2002 Alex Z黳ke
- * Copyright (c) 2002 Gary Jennejohn
- *
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- */
- #include
- #include
- #include
- /*
- *************************************************************************
- *
- * Jump vector table as in table 3.1 in [1]
- *
- *************************************************************************
- */
- .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
OK,首先我们将58行屏蔽掉,然后在59行添加代码:
58: @ .balignl 16,0xdeadbeef
59: .byte 0x11
保存后,用linux上的交叉编译器来编译下,看看什么效果:
eleven@eleven-desktop:~/workspace$ arm-linux-as start.S -o start.o
start.S: Assembler messages:
start.S:41: Error: misaligned branch destination
提示边界对齐出错,这说明arm编译器没有自动帮我们对齐。添加的 .byte 0x11 只占用了1字节,导致其后的全部指令地址都没有对齐,而arm的指令要求32位对齐,不然无法寻址。所以41行处跳转去start_code时出现了问题,start_code此时的存储地址不是4字节的整数倍,当然无法寻址了。
要解决这个问题,这里我们就需要手动对齐了。在59行后添加如下代码:
.align 4,0x12
保存后再编译就顺利通过了。为了能更清楚知道它的作用,我们来看看反汇编代码:
- eleven@eleven-desktop:~/workspace$ arm-linux-objdump -d start.o
- start.o: file format elf32-littlearm
- Disassembly of section .text:
- 00000000 <_start>:
- 0: ea00000e b 40
- 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
- 00000038 <_fiq>:
- 38: 00000000 .word 0x00000000
- 3c: 121212ee .word 0x121212ee
- 00000040
: - 40: e10f0000 mrs r0, CPSR
- 44: e3c0001f bic r0, r0, #31 ; 0x1f
- 48: e38000d3 orr r0, r0, #211 ; 0xd3
- 4c: e129f000 msr CPSR_fc, r0
仔细看37行处,align 4,0x12的作用体现出来了吧?为了能够4字节对齐,添加了0x121212共3字节,这样就保证了start_code开始处的地址为4字节的倍数。
接下来我们将刚刚添加的代码删除掉,恢复至start.S刚裁剪完后的状态(不要忘记将58行处的屏蔽去掉)。之后做如下改动(红色部分为改动部位):
- .globl _start //不占用内存空间
- _start: b start_code //占用4字节
- ldr pc, _undefined_instruction //占用4字节
- ldr pc, _software_interrupt //占用4字节
- ldr pc, _prefetch_abort //占用4字节
- ldr pc, _data_abort //占用4字节
- ldr pc, _not_used //占用4字节
- ldr pc, _irq //占用4字节
- @ ldr pc, _fiq //屏蔽掉了,这里不再占用内存
- _undefined_instruction: .word undefined_instruction //占用4字节
- _software_interrupt: .word software_interrupt //占用4字节
- _prefetch_abort: .word prefetch_abort //占用4字节
- _data_abort: .word data_abort //占用4字节
- _not_used: .word not_used //占用4字节
- _irq: .word irq //占用4字节
- @_fiq: .word fiq //屏蔽掉了,这里同样不占用内存
- .balignl 16,0xdeadbeef
上面一共占用内存为 4字节x7 + 4字节x6 = 52字节。离52最近的16的整数倍地址为64,中间差了12个字节,对齐时需要填充3次的0xdeadbeef,下面我们验证下,看推断对不对。
编译之后再进行反汇编,有如下显示:
- eleven@eleven-desktop:~/workspace$ arm-linux-objdump -d start.o
- start.o: file format elf32-littlearm
- Disassembly of section .text:
- 00000000 <_start>:
- 0: ea00000e b 40
- 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: deadbeef .word 0xdeadbeef
- 38: deadbeef .word 0xdeadbeef
- 3c: deadbeef .word 0xdeadbeef
- 00000040
: - 40: e10f0000 mrs r0, CPSR
- 44: e3c0001f bic r0, r0, #31 ; 0x1f
- 48: e38000d3 orr r0, r0, #211 ; 0xd3
- 4c: e129f000 msr CPSR_fc, r0
.balignl 16,0xdeadbeef 的作用体现出来了,注意看红色部分,与我们的推断相吻合。
到此先告一段落吧,相信经过这样的分析,我应该很久不会忘记.balignl和他那几个“近亲”的作用了,o(∩_∩)o 。
另:4字节对齐是可以理解的,但是关于16字节对齐我还是有些想不通,按照我现在的理解,加不加 .balignl 16,0xdeadbeef 这句都不会影响编译或者寻址的,之前的所有指令都是4字节大小。
网上查阅,找到了两种答案:
1.类似这样的值很多,像0xabababab,它们的作用就是为内存做标记,插在那里,就表示从这个位置往后的一段有特殊作用的内存,而这个位置往前,禁止访问。
2.正如deadbeef一样,其实是作者故意搞笑,无任何实际意义(如果真是这样,我会脸红的。。。)。
你怎样认为?想通了的朋友麻烦留言告知,谢谢。