考虑以下 AVR 程序:
#define F_CPU 500000ul
#include <avr/io.h>
#include <util/delay.h>
// Not used, but it has significance. I suspect its presence is causing some undesired behaviour in debug info
void do_nothing() {
_delay_ms(300);
_delay_ms(300);
}
int main(void) {
DDRD = 0xFF; // Line 13
PORTD = 0x00; // Line 14
PORTD = 0x01; // Line 15
PORTD = 0x00; // Line 16
while (1) {
}
}
非常简单。如果我们在禁用优化并启用调试信息 (-ggdb) 的情况下进行编译,并让 GDB 反汇编操作码,我们将得到以下说明
main()
:
0x000002dc <main+0>: push r28
0x000002de <main+2>: push r29
0x000002e0 <main+4>: in r28, 0x3d
0x000002e2 <main+6>: in r29, 0x3e
0x000002e4 <main+8>: ldi r24, 0x2A
0x000002e6 <main+10>: ldi r25, 0x00
0x000002e8 <main+12>: ldi r18, 0xFF ; 255
0x000002ea <main+14>: movw r30, r24
0x000002ec <main+16>: st Z, r18
0x000002ee <main+18>: ldi r24, 0x2B
0x000002f0 <main+20>: ldi r25, 0x00
0x000002f2 <main+22>: movw r30, r24
0x000002f4 <main+24>: st Z, r1
0x000002f6 <main+26>: ldi r24, 0x2B
0x000002f8 <main+28>: ldi r25, 0x00
0x000002fa <main+30>: ldi r18, 0x01 ; 1
0x000002fc <main+32>: movw r30, r24
0x000002fe <main+34>: st Z, r18
0x00000300 <main+36>: ldi r24, 0x2B
0x00000302 <main+38>: ldi r25, 0x00
0x00000304 <main+40>: movw r30, r24
0x00000306 <main+42>: st Z, r1
0x00000308 <main+44>: rjmp .-2 ; 0x308 <main+44> (offset in GDB is byte offset, not word offset, so RJMP .-2 is actually RJMP .-1 (infinite loop))
现在,让我们关注第 13 行。除非我弄错了,否则该行的 PC(字节地址)范围应该是 0x0000002e4(包含)到 0x000002ee(不包含)。换句话说,我认为第 13 行包含以下指令子集:
0x000002e4 <main+8>: ldi r24, 0x2A
0x000002e6 <main+10>: ldi r25, 0x00
0x000002e8 <main+12>: ldi r18, 0xFF ; 255
0x000002ea <main+14>: movw r30, r24
0x000002ec <main+16>: st Z, r18
我们将端口D的DDR地址加载到r24和r25中,然后将0xFF加载到r18中,然后将DDR地址复制到Z寄存器,然后将r18的值写入该DDR。所有这些都是针对第 13 行的,除非我弄错了。
问题是 DWARF 数据与我的观点不一致。从 ELF 转储 DWARF 数据(使用 dwarfdump),我们得到以下行号信息:
0x000002c8 [ 10, 1] NS uri: "main.c"
0x000002de [ 12,18] NS
0x000002e6 [ 13, 5] NS <-- This seems wrong...
0x000002ea [ 13,10] NS
0x000002f0 [ 14, 5] NS <-- and this...and so on
0x000002f4 [ 14,11] NS
0x000002f8 [ 15, 5] NS
0x000002fc [ 15,11] NS
0x00000302 [ 16, 5] NS
0x00000306 [ 16,11] NS
0x0000030a [ 18,11] NS DI=0x1
0x0000030c [ 18,11] NS ET
所以根据DWARF数据,main.c的第13行从0x0000002e6开始,第14行从0x0000002f0开始,但这似乎不对。应该分别是 0x0000002e4 和 0x0000002ee。
更奇怪的是,未使用的
do_nothing()
函数似乎在其中发挥了作用。如果我将该函数全部删除,则 DWARF 数据是正确的:
0x00000082 <main+0>: push r28
0x00000084 <main+2>: push r29
0x00000086 <main+4>: in r28, 0x3d
0x00000088 <main+6>: in r29, 0x3e
0x0000008a <main+8>: ldi r24, 0x2A
0x0000008c <main+10>: ldi r25, 0x00
0x0000008e <main+12>: ldi r18, 0xFF ; 255
0x00000090 <main+14>: movw r30, r24
0x00000092 <main+16>: st Z, r18
0x00000094 <main+18>: ldi r24, 0x2B
...
0x00000082 [ 12,18] NS uri: "main.c"
0x0000008a [ 13, 5] NS
0x0000008e [ 13,10] NS
0x00000094 [ 14, 5] NS
0x00000098 [ 14,11] NS
0x0000009c [ 15, 5] NS
0x000000a0 [ 15,11] NS
0x000000a6 [ 16, 5] NS
0x000000aa [ 16,11] NS
0x000000ae [ 18,11] NS DI=0x1
0x000000b0 [ 18,11] NS ET
第 13 行从 0x0000008a 开始(第一个
LDI
指令,将 DDRD 加载到 r24 和 r25),第 14 行从 0x00000094 开始,这是正确的。
如果我从未使用的函数体中删除其中一个
_delay_ms()
调用,同样适用:
void do_nothing() {
_delay_ms(300);
// By removing the other _delay_ms() call, the debug info is no longer incorrect.
}
知道这里出了什么问题吗?
我正在使用 GCC 11.1.0,用于 AVR 架构。
这似乎是 GCC 11.1.0 中的回归。此后该问题已得到修复。我升级到 GCC 13.2.0 并且无法重现该问题,所以一切都好。