我正在写一个运行裸机的程序。我试图从一个自定义链接器脚本中写入一个变量。当用-O 0选项编译时,代码运行得很好,但当用-Os选项编译时,代码运行得不像预期的那样。
我使用的代码如下。
主.c:
#define TTB_BASE (&Image$$TTB)
extern unsigned int Image$$TTB;
int main ()
{
*TTB_BASE = 56326;
unsigned int *ttb=TTB_BASE+16;
for (int i = 0; i < 8; i++ )
{
*ttb++ = 1;
}
*(volatile unsigned int*)(0x00100000) = *TTB_BASE;
}
当用-Os选项编译时,代码**TTB_BASE = 56326;* 似乎被优化掉了,并且该值没有被存储到地址TTB_BASE。
使用的连接器指令码:
ARMCA7.ld
ENTRY(main)
SECTIONS
{
. = 0x00160000;
.text : { *(.text*) }
.data : { *(.data .data.* .gnu.linkonce.d*) }
.bss (NOLOAD) : {
. = ALIGN(16);
*(.bss .bss.*)
*(COMMON)
}
. = 0x00170000;
.ttb :
{
Image$$TTB = .;
}
}
正在编译语句:
arm-none-eabi-gcc -mcpu=cortex-a7 -Os -std=gnu99 -c -o main.o main.c
arm-none-eabi-gcc -mcpu=cortex-a7 -Os -T ARMCA7.ld -nostartfiles -Xlinker --gc-sections -Wl,-Map,"main.map" -o main.elf main.o
反汇编得到:
Address : Opcode Statement
------- ------ ---------
8 unsigned int *ttb=TTB_BASE+16;
main:
00160000: 24 30 9f e5 ldr r3, [pc, #36] ; 0x16002c <main+44>
12 *ttb++ = 1;
00160004: 01 10 a0 e3 mov r1, #1
00160008: 20 20 83 e2 add r2, r3, #32
0016000c: 04 10 83 e4 str r1, [r3], #4
10 for (int i = 0; i < 8; i++ )
00160010: 02 00 53 e1 cmp r3, r2
00160014: fc ff ff 1a bne 0x16000c <main+12>
14 *(volatile unsigned int*)(0x00100000) = *TTB_BASE;
00160018: 60 20 13 e5 ldr r2, [r3, #-96] ; 0xffffffa0
0016001c: 01 36 a0 e3 mov r3, #1048576 ; 0x100000
15 }
00160020: 00 00 a0 e3 mov r0, #0
14 *(volatile unsigned int*)(0x00100000) = *TTB_BASE;
00160024: 00 20 83 e5 str r2, [r3]
15 }
00160028: 1e ff 2f e1 bx lr
0016002c: 40 00 17 00 andseq r0, r7, r0, asr #32
可以看出,在链接器脚本中定义的地址TTB_BASE为0x 00170000,没有写入任何内容,写入地址0x 00100000的值是错误的。
代码或链接器脚本中是否存在错误?
备注:
如果将 main.c 的前两行代码更改为以下代码,
#define TTB_BASE (Image$$TTB)
extern unsigned int Image$$TTB[];
代码可以被正确编译,地址TTB_BASE也写得正确。但是,正如我从other questions看到的,这两种方法都应该是正确的。
使用的gcc版本:
arm-none-eabi-gcc --version
arm-none-eabi-gcc.exe (Arm GNU Toolchain 11.3.Rel1) 11.3.1 20220712
Copyright (C) 2021 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.
我还使用arm-none-eabi-gcc.exe 10.3.1 20210621(发布版本)测试了代码,并得到了相同的问题。
2条答案
按热度按时间bmp9r5qi1#
您的外部变量对编译器没有可见的 * 副作用 *,因此允许优化赋值。
避免这种情况的标准方法是添加限定符
volatile
。它告诉编译器该变量有副作用。因此,它不能优化赋值。如果您有多个线程(如中断服务例程),这些线程彼此共享变量或与主线程共享变量,则也需要此限定符。
增加:
正如其他人在评论中指出的那样,链接器脚本中有一个错误,你没有为变量保留空间。
zfycwa2u2#
我发现发布的问题与变量引用有关,而不是与链接器脚本有关。请参考此new post以获得进一步的讨论。
作为一个结论,代码不起作用的真实的原因如下。
&Image$$TTB+16
是 * Undefined Behavior in C *。也就是说,如果要更改链接器脚本文件中定义的地址区域,则需要以下声明。
下面是另一个有同样bug的示例代码。注意,它应该用-O 1或更高版本编译。你可以尝试一下at Compiler Explorer。
如果执行此代码,可能会得到以下输出: