Компиляция с -flto и -nostartfiles приводит к отсутствию какой-либо программы.

При разработке собственного загрузчика я столкнулся с проблемой в avr-gcc. Пытаюсь скомпилировать код с флагами компилятора

-flto          #для включения оптимизации времени компоновки
-nostartfiles  #чтобы исключить таблицу векторов прерываний

Мой загрузчик состоит из нескольких файлов, которые используются в зависимости от флагов компиляции. Все исходники компилируются и линкуются, а путь к коду ведёт только к некоторым их частям. Поэтому я хотел избавиться от мёртвого кода, и -flto (будучи более новой разработкой) создаёт гораздо меньший объём машинного кода, чем -ffunction-sections -fdata-sections с -Wl,--gc-sections -Wl,--print-gc-sections.

Но когда дело доходит до дальнейшего сокращения размера машинного кода путем удаления таблицы векторов прерываний с помощью -nostartfiles, весь код оптимизируется, оставляя мне на выходе нулевой байт.

Сталкивался ли кто-нибудь с такой же проблемой при использовании этой комбинации флагов компилятора?

, 👍1

Обсуждение

Я столкнулся с тем, что больше не могу скомпилировать скетч Arduino с помощью LTO, потому что оптимизация была настолько агрессивной, что исключала некоторые обработчики прерываний периферийных устройств. Возможно, GCC считает, что начальная функция загрузки (по адресу 0x0000 или где-то ещё) никогда не вызывается, и всё завершает? Может быть, попробовать добавить __attribute__ ((used)) к некоторым функциям и посмотреть, что получится? (https://gcc.gnu.org/wiki/LinkTimeOptimizationFAQ), @Maximilian Gerhardt

Вот и всё! Теперь он выдаёт результат. Но он больше не меньше, чем версия --gc-sections. Полагаю, он уже удалил некоторые части, которые должны были остаться. Спасибо!, @Kwasmich

Рад слышать! Было бы здорово, если бы вы сами ответили на вопрос и привели конкретный фрагмент кода, который помог всё это заработать., @Maximilian Gerhardt


1 ответ


Лучший ответ:

1

Как написал @MaximilanGerhardt в своём комментарии, LTO оптимизировал слишком много кода. Обычные программы начинаются с таблицы векторов прерываний, которая в случае обычного сброса или запуска указывает на функцию main. Поэтому компилятор знает, что у main есть точка входа, и она используется. Сама программа начинается с этой таблицы.

Удаление таблицы векторов прерываний с помощью -nostartfiles приводит к тому, что компилятор вообще не знает об использовании main и оптимизирует ее, убирая.

Чтобы дать компилятору подсказку, __attribute__((used)) должен быть помещен во все методы, которые должны остаться, и в противном случае не должно быть никакого пути кода, ведущего к ним.

,

Вы наткнулись на что-то работающее, но это не совсем верное решение. Вам следует использовать скрипт линковки (традиционно ld-файл), который помещает ваш стартовый код (желательно обёртку, а не сам main()) по правильному адресу с атрибутом «keep». В случае AVR «векторы» — это на самом деле инструкции перехода, поэтому, если вам нужна поддержка только команды сброса, вы, вероятно, можете просто начать свой код с неё; в других архитектурах векторы — это указатели, поэтому вам потребуется указать адрес стартового кода, а затем сам код., @Chris Stratton

Также следует отметить, что без кода запуска инициализированные переменные ОЗУ работать не будут, поскольку копирование этой инициализации из флэш-памяти в ОЗУ выполняется кодом запуска; однако значения типа PROGMEM будут работать., @Chris Stratton