LINUX.ORG.RU

GCC: как защититься от «оптимизации» конструкторов?

 


1

2

Здравствуйте.

Часть кода у меня в проекте выполенена в виде «модулей», то есть, исходник содержит функцию с __attribute__ ((constructor (xx))), выполняемую один раз при старте, которая помещает точки входа модуля в таблицу вызовов. Поскольку ни одна из функций модуля не вызывается явно из других частей программы, оптимизатор при линковке весь это модуль успешно выкидывает. Как сказать линкеру, чтобы он этого не делал?


Ответ на: комментарий от r702

Создай map file для модуля, в котором явно пропиши все публичные методы.

edit: Давно не использовал GNU ld, похоже что тут это называется «version script».

dsdqmhsx ()
Последнее исправление: dsdqmhsx (всего исправлений: 1)
Ответ на: комментарий от dsdqmhsx

Это затруднительно. Во-первых, все функции в модулях - static. Имена некоторых функций в различных модулях одинаковые. Во-вторых, разработка ведется в среде, которая не позволяет редактировать map-ы, она их генерирует «на ходу». Буду гуглить, что за «version script».

r702 ()
Ответ на: комментарий от r702

А атрибутом отключить оптимизацию функций помогает? И какой реально язык и линковщик? Какие опции линковки?

slapin ★★★★★ ()
Ответ на: комментарий от slapin

Каким атрибутом отключить оптимизацию функции?

Помогает, реально, костыль: если добавить в исходник пустую функцию, и вызвать ее из не-модуля. Тогда все остается на своих местах.

Компилятор riscv32-esp-elf-gcc-8.4.0

Флаги при сборке, если это что-то даст,

-ffunction-sections -fdata-sections -Wall -Werror=all -Wno-error=unused-function -Wno-error=unused-variable -Wno-error=deprecated-declarations -Wextra -Wno-unused-parameter -Wno-sign-compare -ggdb -Wno-error=format= -nostartfiles -Wno-format -Os -freorder-blocks -fstrict-volatile-bitfields -Wno-error=unused-but-set-variable -fno-jump-tables -fno-tree-switch-conversion -fno-stack-protector -std=gnu99 -Wno-old-style-declaration -D_GNU_SOURCE "

у линкера:

-nostartfiles -march=rv32imc --specs=nosys.specs

Что интересно, при сборке под другие системы: linux, Windows, чисто ембеддерские, - более старыми версиями gcc, проблема отсутствует.

r702 ()
Ответ на: комментарий от r702
__attribute__((optimize("-O0")))

например. А линкер стандартный гнутый или какой специальный? Я бы засунул функции в индивидуальные секции и .map файл бы посмотрел. Возможно банальный баг линкера…

slapin ★★★★★ ()
Последнее исправление: slapin (всего исправлений: 1)
Ответ на: комментарий от slapin

Я так понимаю, этот атрибут запрещает оптимизацию тела функции, а не ее существование. Не помогло.

линкер говорит, что он GNU ld (crosstool-NG esp-2021r2) 2.35.1.20201223

Логика линкера а принципе понятна. «Кого мы тут можем вызвать? Никого? Тогда на выход». Проблема тут может быть в следующем: не надо выкидывать конструкторы. Даже если они static. Попробую еще пробится к авторам этой ветки gcc.

r702 ()
Ответ на: комментарий от r702

__attribute__((constructor, used, noinline)) еще можно попробовать. Но вряд ли поможет.

SZT ★★★★★ ()
Последнее исправление: SZT (всего исправлений: 1)

у гцц есть флаг -fkeep-static-functions. тогда он не будет выкидывать статические функции без обращения.

но писать функции статиками, а потом отменять их статичность флагом…это какая-то порочная логика, имхо.

alysnix ★★ ()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от r702

Дык конструкторы вызываются из спецсекции то есть они таки используются, потому это или баг в линкере или в скрипте линковки используемого тулчейна или включены опции с агрессивной оптимизацией при линковке. Попробуй слинковать вручную свои .o без опций и посмотри полученный elf на предмет секций которые в нем остались. Все секции которые у тебя есть в .o должны быть в результирующем файле. Я вангую что секция с констукторами удаляется в скрипте или не участвует в линковке. Так же стартовый скрипт может не вызывать конструкторы из секции (если она таки есть).

slapin ★★★★★ ()
Ответ на: комментарий от xaizek

-ffunction-sections будет полезен для отслеживания.

slapin ★★★★★ ()
Ответ на: комментарий от alysnix

ну обращение как бы есть из секции конструктора

slapin ★★★★★ ()

Флажком whole-archive.

Laz ★★★★★ ()

Вообще может (хотя не уверен) это какой-то баг компилятора. Но что, если изменить архитектуру?

Пусть ваш модуль называется foo. Сделать функции __foo_init() и __foo_deinit() какие-нибудь, которые вызывает основная программа при подключении модуля. Т.е. есть функция load_plugin(), которая делает dlopen модуля, а потом ищет нужные функции и сама их вызывает? Не вариант?

anonymous ()
Ответ на: комментарий от anonymous

Там я как понимаю эмбедка, но можно действительно завести common-секцию и в ней вызывать все функции через инициализатор, (только сейчас надо явно указывать что используются common-секции, эти дегенераты^W странные люди решили их внезапно дропнуть…) с common-секциями не нужно будет формировать список инициализации вручную.

slapin ★★★★★ ()
Ответ на: комментарий от alysnix

поддержу это мнение. тоже непонятно, зачем объявлять статики и потом возмущаться, что их не видно снаружи. их и не должно быть видно снаружи.

Iron_Bug ★★★★★ ()

исходник содержит функцию с __attribute__ ((constructor (xx))), выполняемую один раз при старте, которая помещает точки входа модуля в таблицу вызовов

Не надо так делать. (Никто не сказал за 17 постов - мельчает лор.) Лучше прямо в модуле явно проинициализировать таблицы. Если тебе нужны какие-то функции / модули опционально, скрой их препроцессором.

LamerOk ★★★★★ ()

Зачем вообще attribute ((constructor (xx))), когда есть поддерживаемое в стандарте решение:



static volatile int nothing = [](){

your_constructor_code

return 0;
}();

«volatile» здесь по стандарту даже лишний

next_time ★★★★★ ()

А это тебе - за то, что не уважаешь C99. Хипстеры должны страдать ☺

Я вот, например, гору кода написал на С, но впервые вообще слышу про какую-то херь типа "конструкторов" в этом ЯП! Я уж по названию думал, что ты вообще о крестах, но тег мимо поставил!!!

anonymous ()
Ответ на: комментарий от anonymous

Эдик, это не то чтобы гццшная магия, но точно нестандарт, хотя шланг поддерживает. То что ты об этом не слышал, больше говорит о тебе, нежели о гцц.

Dark_SavanT ★★★★★ ()
Ответ на: комментарий от r702

Имена некоторых функций в различных модулях одинаковые.

Возможно дело в этом, если, например, твои конструкторы (или что там у тебя не работает) в разных модулях называются одинаково, то линкер берёт только первый попавшийся вариант.

Вообще, не очень понятно, что у тебя оно там не линкует. Привёл бы минимальный пример, тебе бы сразу подсказали.

У линкера ещё есть опция --gc-keep-exported, которая запрещает выбрасывать секции с глобальными символами при --gc-sections.

anonymous ()
Ответ на: комментарий от anonymous

Я вот, например, гору кода написал на С, но впервые вообще слышу про какую-то херь

Чем дальше, тем больше убеждаюсь, что в душе Эдуард — прирождённый ардуинщик. Может ты и про атрибуты первый раз слышишь? А про линкер что-нибудь слышал? Ахвхв

anonymous ()
Ответ на: комментарий от Dark_SavanT

Было бы простительно, если бы он не лез агрессивно в программерские темы, зачастую даже не поняв вопроса.

anonymous ()
Ответ на: комментарий от xaizek

на C тоже самое будет, только вместо лямбды - именованая функция

[code=cpp]

static int constructor(){

your_constructor_code

return 0; };

static volatile int nothing = constructor();

[/code]

next_time ★★★★★ ()
Последнее исправление: next_time (всего исправлений: 2)
Ответ на: комментарий от next_time

Не выйдет, в C инициализаторы глобальных/статических переменных должны быть константными выражениями.

xaizek ★★★★★ ()
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.