为什么GCC生成的代码带有一堆无用的JMP指令?

xan*_*ndy 3 assembly avr avr-gcc

我目前正在Atmel Studio中为Atmega328P(Arduino UNO)开发一个引导加载程序,从反汇编中我发现了以下代码(我的引导加载程序从0x3800开始):

--- ../../../../crt1/gcrt1.S ---------------------------------------------------
00003800  JMP 0x00003834        Jump 
00003802  JMP 0x00003979        Jump 
00003804  JMP 0x00003979        Jump 
00003806  JMP 0x00003979        Jump 
00003808  JMP 0x00003979        Jump 
0000380A  JMP 0x00003979        Jump 
0000380C  JMP 0x00003979        Jump 
0000380E  JMP 0x00003979        Jump 
--- ../../../../crt1/gcrt1.S ---------------------------------------------------
00003810  JMP 0x00003979        Jump 
00003812  JMP 0x00003979        Jump 
00003814  JMP 0x00003979        Jump 
00003816  JMP 0x00003979        Jump 
00003818  JMP 0x00003979        Jump 
0000381A  JMP 0x00003979        Jump 
0000381C  JMP 0x00003979        Jump 
0000381E  JMP 0x00003979        Jump 
00003820  JMP 0x00003979        Jump 
00003822  JMP 0x00003979        Jump 
00003824  JMP 0x00003979        Jump 
00003826  JMP 0x00003979        Jump 
00003828  JMP 0x00003979        Jump 
0000382A  JMP 0x00003979        Jump 
0000382C  JMP 0x00003979        Jump 
0000382E  JMP 0x00003979        Jump 
00003830  JMP 0x00003979        Jump 
00003832  JMP 0x00003979        Jump 
--- C:\Users\andy\Documents\Atmel Studio\7.0\CannyFlashy\CannyFlashy\CannyFlashy\Debug/.././Sketch.cpp 
int main(){
00003834  IN R28,0x3D       In from I/O location 
Run Code Online (Sandbox Code Playgroud)

为什么 GCC 会生成这样的代码?是否有办法避免它们?

Mep*_*stt 5

它是什么

\n\n

正如Javier Silva Ort\xc3\xadz在评论中提到的,它是一个放置在程序存储器最开头的中断表。

\n\n

第一条指令是一个RESET向量,我们可以看到它指向的地址。换句话说,复位后,MCU 立即执行第一JMP 0x00003834条指令并准确移动到程序的开头。

\n\n

顺便说一下,看看其他JMP指令:它们都指向同一个地址,即所谓的__bad_interrupt(),唯一的指令RETI就放置在那里。如果这些中断之一意外命中(并且您的程序不知道如何处理该中断),则单RETI条指令会立即完成该中断,并且程序将返回到正常执行。

\n\n

避免它

\n\n

如果您绝对确定引导加载程序中没有可能的中断命中,则可以避免中断表,将CFLAGS = -nostartfiles标志传递给编译器。

\n\n

当心!

\n\n

其他一些东西会被-nostartfilesflag破坏!

\n\n

问题是 avr-gcc 在编译程序的开头产生了一些魔力。编译器将一些常见操作放在中断表之后。这些操作按__ctors_end__do_copy_data__do_clear_bss等部分划分。需要它们做什么?

\n\n

首先,在节r1期间将寄存器设置为零__ctors_end。为什么它如此重要?r1每次编译器将其他寄存器与零进行比较时默认使用。是的,avr-gcc知道这个“神奇”r1并且不会写入它,但是在某些情况下,当它发生变化时(例如,乘法的结果放置在r0:r1寄存器对中),并且每次它改变时更改后,avr-gcc 会取消它。r1如果在启动过程中未将其设置为零,一切都可能会出错......

\n\n

程序开始时完成的另一件事是将应该为零的变量设置为零。这是在该__do_clear_bss部分完成的。试想一下,您期望某个值为零,但事实并非如此。后果是什么?

\n\n

另一个重要的操作是将数据从程序存储器复制到SRAM。这是在__do_copy_data部分完成的。关闭__do_copy_data会破坏所有静态阵列。

\n\n

和?..

\n\n

您应该非常小心地尝试使用-nostartfiles. 仅当您 200% 确定发生了什么时才执行此操作。

\n