Почему GCC генерирует код с кучей бесполезных инструкций JMP? - PullRequest
2 голосов
/ 19 октября 2019

В настоящее время я работаю над загрузчиком в 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такой код и есть ли вообще их избежать?

1 Ответ

2 голосов
/ 15 ноября 2019

ЧТО ЭТО

Как и Хавьер Сильва Ортиз , упомянутый в комментарии, это таблица прерываний, расположенная в самом начале памяти программы.

Первая инструкция - это вектор RESET, и мы можем видеть адрес, на который она указывает. Другими словами, сразу после сброса MCU выполняет первую инструкцию JMP 0x00003834 и перемещается точно в начало программы.

Кстати, взгляните на другие инструкции JMP: все ониуказывая на тот же адрес, так называемый __bad_interrupt(), где находится единственная инструкция RETI. Если одно из этих прерываний случайно срабатывает (и ваша программа не знает, что делать с этим прерыванием), то одиночная инструкция RETI мгновенно завершает это прерывание, и программа возвращается к нормальному выполнению.

ИЗБЕГАЯ ЭТОГО

Если вы абсолютно уверены, что в вашем загрузчике нет возможных прерываний, вы можете избежать таблицы прерываний, передавая флаг 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% уверены, что происходит.

...