ALIGN的烦恼和传说中的秘技
风起
一个ADuC702x(ARM7TDMI)的系统,编译后运行发现这样的问题:
// Begin main.c // Some code here... unsigned char bcd[14] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13};
void pro_init(void) { // Some code here... }
int main(void) { // Some code here... } // End main.c
gcc下编译汇报... -------- begin (mode: ROM_RUN) -------- arm-elf-gcc (GCC) 4.1.2 (WinARM 4/2007) Copyright (C) 2006 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Size before: main.elf : section size addr .text 436 524288 .data 14 65536 .comment 34 0 Total 484
Errors: none -------- end --------
云涌
结果main()中测试发现:bcd_buf[0]~bcd_buf[11]有对应初值,而bcd[12]和bcd[13]的值 为0。显然,编译结果有BUG! 经过多次实验,发现改定义为bcd[16],那么bcd[0]至bcd[15] 都有对应初值。gcc编译结果如下: Size before: main.elf : section size addr .text 436 524288 .data 16 65536 .comment 34 0 Total 484 可以看到.data变为16....也就说这是ALIGN(ARM7的RAM是4byte对齐的)方面的BUG.虽然有 这个暂时的回避办法,但这个滥竽充数的招数严重影响我们境界的提升,我们需要更深探索 本源...
惊涛
1)下载gcc使用文档....没找到解法... 2)看startup.S的汇编启动文件... #ifdef ROM_RUN # Relocate .data section (Copy from ROM to RAM) LDR R1, =_etext LDR R2, =_data LDR R3, =_edata CMP R2, R3 BEQ DataIsEmpty LoopRel: CMP R2, R3 LDRLO R0, [R1], #4 STRLO R0, [R2], #4 BLO LoopRel DataIsEmpty: #else #warning RAM_RUN - .data will not be copied #endif # Clear .bss section (Zero init) MOV R0, #0 LDR R1, =__bss_start__ LDR R2, =__bss_end__ LoopZI: CMP R1, R2 STRLO R0, [R1], #4 BLO LoopZI 我们从代码中可以猜出有初值的全局变量被定位在flash,然后拷贝到ram,紧跟着把没有 初值的全局变量(.bss段)全清0...,那么可以断定没有ALIGN的bcd[12]和bcd[13]应该 是被砍头(ALIGN)后清0了... 3)天啊,又要学汇编... 这些定义(_etext,_edata...)在哪里呢?终于找到了,在ADuC7021-ROM.ld 4)我倒!还要学LD Script...来看看ADuC7021-ROM.ld... SECTIONS {
/* first section is .text which is used for code */
.text : { *startup.o (.text) /* Startup code */ *(.text) /* remaining code */ *(.glue_7t) *(.glue_7) } >IntFLASH . = ALIGN(4);
/* .rodata section which is used for read-only data (constants) */
.rodata : { *(.rodata) } >IntFLASH . = ALIGN(4);
_etext = . ; PROVIDE (etext = .);
/* .data section which is used for initialized data */
.data : AT (_etext) { _data = . ; *(.data) SORT(CONSTRUCTORS) } >IntRAM . = ALIGN(4);
/* take empty data section into account: */ _edata = . ; PROVIDE (edata = .); /* .bss section which is used for uninitialized data */
.bss : { __bss_start = . ; __bss_start__ = . ; *(.bss) *(COMMON) } >IntRAM . = ALIGN(4); __bss_end = . ; __bss_end__ = . ;
_end = .; PROVIDE (end = .);
/* Stabs debugging sections. */ /* 省略n字 */ } 显然,ALIGN(4)没有起作用,否则,gcc编译应该看到.data不是14而是16... 我请教了n个gcc的高手(至少我认为是)了,可其中90%的都没自己改过 ld script文件...看来我要做剩下的10%...我改...我改...我改改改...
SECTIONS {
/* first section is .text which is used for code */
.text : { *startup.o (.text) /* Startup code */ *(.text) /* remaining code */ *(.glue_7t) *(.glue_7) . = ALIGN(4) } >IntFLASH
/* .rodata section which is used for read-only data (constants) */
.rodata : { *(.rodata) . = ALIGN(4) } >IntFLASH
_etext = . ; PROVIDE (etext = .);
/* .data section which is used for initialized data */
.data : AT (_etext) { _data = . ; *(.data) SORT(CONSTRUCTORS) . = ALIGN(4) } >IntRAM
/* take empty data section into account: */ _edata = . ; PROVIDE (edata = .);
/* .bss section which is used for uninitialized data */
.bss : { __bss_start = . ; __bss_start__ = . ; *(.bss) *(COMMON) . = ALIGN(4) } >IntRAM __bss_end= . ; __bss_end__ = . ;
_end = .; PROVIDE (end = .);
/* Stabs debugging sections. */ /* 省略n字 */ } 变换ALIGN(4)位置后,将bcd[16]定义改回bcd[14],看看gcc编译结果...
彩虹
Size before: main.elf : section size addr .text 436 524288 .data 16 65536 // 注意这里 .comment 34 0 Total 484
下载文件,观察bcd[12]和bcd[13],果然有我们对应的初值了... 兴奋之余,默然回首,发现学了n多东东... gcc的编译参数和Makefile、 arm汇编语言、ld script、obj定位.....
花絮
回头看WinARM的example下所有ld script文件都存在类似的问题... 再想想,如果没有ld script文件,又或startup.S以库形式提供,有没有其它 简便的解决办法呢?有!传说中的秘技:“不要给定义在程序函数外的 全局变量赋初值,而是用单独的初始化函数来在程序内部赋值...” 我想当初第一个说这话的人一定有他的道理,但经过这番折腾后,我 明白了其中大部分的含意,我的境界也许没他高,但对这个问题的看法, 我相信我们的境界大致是一样的...
感谢大家一起分享...
2007-12-5 wolver@21com.com
|