为什么GCC会生成大量无用的JMP指令?

3

我目前正在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 

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


4
那些JMP指令是中断向量表。在使用Atmel Studio从C代码编译时,它们会自动生成。我记得在我的AVR编程书籍中曾经读到过如何将其移除。我需要查一下并与您联系。 - Javier Silva Ortíz
@JavierSilvaOrtíz 谢谢,我会研究一下那些IVT。 - xandy
1个回答

5

什么是中断表

正如 Javier Silva Ortíz在评论中提到的,中断表位于程序存储器的最开始位置。

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

顺便说一下,看看其他的JMP指令:所有这些指令都指向同一个地址,即所谓的__bad_interrupt(),只有一条指令RETI被放置在其中。如果其中一个中断意外发生(并且您的程序不知道如何处理此中断),那么单个RETI指令将立即终止此中断,并使程序返回正常执行状态。

避免中断表

如果您确信在Bootloader中不可能有任何中断,则可以通过将CFLAGS = -nostartfiles标志传递给编译器来避免中断表。

请小心!

使用-nostartfiles标志将破坏其他一些内容!

问题在于avr-gcc在编译程序开头做出了一些魔法。编译器在中断表之后放置了一些常见的操作。这些操作由各个部分分段,例如__ctors_end__do_copy_data__do_clear_bss等等。他们为什么需要呢?

首先,在__ctors_end部分期间将r1寄存器设置为零。为什么这很重要?默认情况下,每当编译器将其他寄存器与零进行比较时,都会使用r1寄存器。是的,avr-gcc知道这个“神奇”的r1,并且不会对其进行写入,但是有一些情况它会更改(例如,乘法的结果被放置在r0:r1寄存器对中),每次它更改时,avr-gcc将其归零。如果启动过程中未将r1设置为零,则可能会出现故障。

程序开始时执行的另一件事是将应该为零的变量设置为零。这是在__do_clear_bss部分完成的。想象一下,您期望某些内容为零,但实际上不是零。这会带来什么后果?

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

最后..

如果你不确定优化中断表的操作,使用-nostartfiles时要非常小心。只有当你对此非常确定时才应该这样做。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接