stm32 编译出的bin文件一定是4字节的倍数吗?
最近在研究固件升级,在烧写内部FLASH时突然产生一个问题编译出的bin文件一定是4字节的倍数吗?如果不是那么以bin文件总长度除以4的方式写入flash就有可能舍掉了最后的余数。
在stack overflow上得到的答案是:正常情况下编译产生的bin文件是4的倍数,但是并不一定是4字节的倍数,4字节对齐只是因为方便,实际取决于连接脚本等文件的设置。
如果我们打开连接脚本文件(.ld)的话,会看到很多声明如下所示
. = ALIGN(4);
它们指示链接器将当前输出地址提升到可被4整除的值。
举例如下,如果我创建一个空工程,删除以下连接脚本中的align语句
1 ENTRY(Reset_Handler)
2 __stack = 0x20014000;
3 MEMORY
4 {
5 FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
6 RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 80K
7 }
8 SECTIONS
9 {
10 .isr_vector :
11 {
12 . = ALIGN(4);
13 KEEP(*(.isr_vector))
14 . = ALIGN(4);
15 } >FLASH
16 .text :
17 {
18 *(.text)
19 *(.text*)
20 } >FLASH
21 .rodata :
22 {
23 *(.rodata)
24 *(.rodata*)
25 } >FLASH
26 _sidata = LOADADDR(.data);
27 .data :
28 {
29 _sdata = .;
30 *(.data)
31 *(.data*)
32 _edata = .;
33 } >RAM AT> FLASH
34 .bss :
35 {
36 _sbss = .;
37 *(.bss)
38 *(.bss*)
39 *(COMMON)
40 _ebss = .;
41 } >RAM
42 }
以上连接脚本源程序如下:
void Reset_Handler(void) {
while(1)
;
}
用-nostartfiles -nodefaultlibs -nostdlib编译连接这个程序,把标准库相关的文件都删除掉,最后得出的结果如下:
arm-none-eabi-size --format=berkeley "unaligned.elf"
text data bss dec hex filename
320 0 0 320 140 unaligned.elf
可以被4整除,然后我在代码中加入一个char型的变量,如下所示:
1 volatile char c = 0x42;
2 void Reset_Handler(void) {
3 while(1)
4 c+=1;
5 }
重新编译连接得到结果是:
Invoking: Cross ARM GNU Print Size
arm-none-eabi-size --format=berkeley "unaligned.elf"
text data bss dec hex filename
336 1 0 337 151 unaligned.elf
Finished building: unaligned.siz
文件大小不在对4字节对齐。
进一步说明指令是按照16位对齐的。
以Cortex-M作为内核的MCU 如STM32系列使用Thumb2指令集,指令集中的指令有16位也有32位。这导致第一个程序最后是4字节的倍数。当我加入一个 nop指令后
1 void Reset_Handler(void) {
2 asm("nop");
3 while(1)
4 ;
5 }
编译结果比第一个程序大了两个字节,如下:
Invoking: Cross ARM GNU Print Size
arm-none-eabi-size --format=berkeley "unaligned.elf"
text data bss dec hex filename
322 0 0 322 142 unaligned.elf
Finished building: unaligned.siz
补充说明:
有人觉得可以把STM32 MCU切换的16位的模式就会得到16位对齐的bin文件。当你真的切换到16位模式确实很可能最后的出的bin文件是2字节的倍数了,但你可能不完全理解这意味着什么。“32位模式”和“16位模式”通常指指针或寄存器大小,而不是指令大小。例如,AMD x64指令集可能被称为“64位模式”,即使其指令长度可变。有些x64指令只有一个字节。