Открытый канал ID 72525990801502

C++ Embedded

Вся правда о C++ в embedded проектах

Публикации за период

По реакциям

Поиск и вся лента — после бесплатной регистрации. Свои даты — по подписке; владелец канала видит их бесплатно. Сортировка по реакциям — по подписке. Зарегистрироваться

Загрузка…

__cxa_atexit
Еще как причем! Эта __cxa_atexit — не просто ценный мех, а еще внутренняя низкоуровневая функция рантайма C++. С ее помощью компилятор тихой сапой регистрирует деструкторы глобальных и статических объектов, чтобы они были корректно вызваны при завершении работы программы или при выгрузке динамической библиотеки.
Есть похожая функция в чистом C - atexit, но она не очень умеет с динамическими объектами работать, да и с деструкторами в общем-то не особо.
Вот, например, пишем такой глобальный объект

struct MyClass { ~MyClass() {}};
MyClass global_object;

Тогда GCC, никого не спрашивая, нагенерирует неявных вызовов под это дело

:
__cxa_atexit((void (*)(void *))&MyClass::~MyClass, &global_object, &__dso_handle);

В so-библиотеке этот деструктор сохраняется во внутренний список с соответствующим маркером __dso_handle.
Если мы вообще закрываем приложение, будет вызвана функция __cxa_finalize(&__dso_handle), а __dso_handle в этом случае нулевая. В таком случае Itanium C++ ABI велит вызвать последовательно все функции из расстрельного списка.
Гораздо веселее выгружать только so-шник. Это вызывает кратковременный dlclose, который в свою очередь дергает __cxa_finalize с __dso_handle, и тут уже она нулю не равна. Вот тогда мы вытаскиваем из списка все деструкторы с таким маркером и начинаем яростно, дико вызывать в порядке живой очереди.
Для сурового embedded я видел советы отключить этот механизм как избыточный, поскольку микроконтроллерам все равно не хочется жонглировать so-либами.
В GCC (и даже в clang) можно явно отключить этот механизм через флаг -fno-use-cxa-atexit.
В этом случае компилятор перестанет использовать __cxa_atexit, а указатели на деструкторы запишет в специальную секцию бинарного файла .fini_array (или .dtors), которые будут последовательно вызваны при выходе из программы самым простым циклом.
Впрочем, я не проверял. Должно работать.
Просмотры: 10
Перейти в пост Открыть в MAX Сообщить о неточности
__dso_handle
Таинственная незнакомка неизменно появлялась в Начале. Тенью мелькала она среди невзрачных рабочих и прочих безликих ассистентов. Если специально не искать ее взглядом, то и не заметишь. Впрочем, никому и в голову не приходило искать ее специально. Серые мыши никого не интересуют.
Так думал и я, пока не столкнулся с ней в Конце. После этой встречи я не переставал думать о ней. Меня не обманула ее ложная скромность. Это была кротость омута, таящего в себе мрачную тайну. Кто она, какова ее роль во всем этом? Зачем она приходит в Начале и что делает в Конце? Никто не знал.
Я стал следить за ней, записывать, когда и где она появляется. Долгими бессонными ночами я строил планы, где и как подкараулить ее.
Сегодня наконец-то она попалась в мои силки. Теперь она расскажет нам все!
Я пристально смотрел на нее, а она - на меня. Таинственная глобальная переменная __dso_handle.
Если попробовать вывести ее значение в обычном исполняемом файле

printf("%p", __dso_handle);

Она покажется нам банальной пустышкой: (nil).
Нулевой указатель. Тогда зачем она вообще нужна? Совсем эти яйцеголовые разработчики накурились своих мануалов и айда сувать всякую гадость в свои __cxa_thread_atexit.
Однако я сам плюсовик, разрабатываю уже сто лет, внук председателя колхоза. Просто поверьте, тут не все так однозначно!..
Я специально __dso_handle не объявлял, вы, наверняка, тоже. Тогда кто же это сделал?
Компилятор, конечно! Он же обеспечивает вызов и управление глобальными конструкторами и деструкторами в файлах crtbegin (C Runtime Begin) и там же определяет эту магическую переменную. Для тривиальной сборки исполняемого файла компилятор будет применять файлы crtbegin, где

void *__dso_handle = 0;

Для динамических библиотек будет применен особый файл crtbeginS.o, где уже


void *__dso_handle __attribute__ ((__visibility__ ("hidden"))) = &__dso_handle;

Обязательно нужно пометить символ как hidden, чтоб не засветить снаружи динамической библиотеки. В итоге, даже если мы загрузим 100500 so-объектов, у всех будет разная __dso_handle. Это наводит нас на очевидную мысль, что подобный символ можно использовать как некий маркер бинарного модуля.
Эта же мысль пришла в головы создателям требований Itanium C++ ABI.
В разделе 3.3.6.3
Runtime
API в пункте C. Termination ангельским языком писано:
При компоновке любого разделяемого объекта (DSO), содержащего вызов __cxa_atexit, компоновщик должен определить скрытый символ __dso_handle, значением которого является адрес в одном из сегментов объекта. Конкретный адрес не важен, если для разных DSO они различаются.
Казалось бы, причем тут какая-то функция?
Просмотры: 17
Перейти в пост Открыть в MAX Сообщить о неточности
ВКонтакте Телеграм Одноклассники
Избранное в datool.ru

Следите за каналом в личном кабинете

Зарегистрируйтесь в datool.ru, чтобы добавить канал «C++ Embedded» в избранное и отслеживать его показатели.

Зарегистрироваться
Для владельцев каналов

Подтвердите свой канал

Войдите или зарегистрируйтесь в datool.ru, затем добавьте нашего бота в канал по инструкции в личном кабинете. После проверки владения замок исчезнет и вам станет доступно управление каналом.

Войти или зарегистрироваться

Сообщить о неточности

Опишите, что нужно исправить. Если есть источник, добавьте ссылку.

Вы покидаете datool.ru

Ссылка ведёт на внешний сайт. datool.ru не отвечает за его содержимое — проверьте адрес, прежде чем вводить пароли или платёжные данные.

Переход через 10 с

Перейти