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

问题描述 投票:2回答:1

我目前在Atmel Studio中为Atmega328P(Arduino UNO)制作Bootloader,从反汇编中找到了以下代码(我的Bootloader从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会生成这样的代码,总之有避免它们的方法?

assembly avr avr-gcc jmp
1个回答
0
投票

这是什么

如注释中提到的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等部分划分。他们需要什么?

首先,在r1部分中,__ctors_end寄存器被设置为零。为什么这么重要?每次编译器将其他寄存器与零进行比较时,默认情况下都使用r1。是的,avr-gcc知道此“魔术” r1并且不会写入,但在某些情况下,当它更改时(例如,乘法结果放在r0:r1寄存器对中),并且每次更改时,avr-gcc都会将其无效。如果在启动过程中未将r1设置为零,则一切都会出错...

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

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

AND?..

[使用-nostartfiles优化中断表时应格外小心。仅在您200%确定发生了什么情况时,才执行此操作。

© www.soinside.com 2019 - 2024. All rights reserved.