предистория такая, собираюсь сдать статью в известный в нашем круги журнал электронного толка, по ходу статьи приводится мой код и соответственно пример можно будет скачать для повторения экспериментов. Встал вопрос - нада допилить его так чтоб нестыдно было людям показывать - лишнее выкинуть, камменты добавить и вообще ... и покатилось. Нашел много не точтобы ошибок - код то работает но..
один из обнаруженных косяков - решил в коде для красоты и стройности заиспользовать функцию с атрибутом __attribute__((constructor)), компиляю запускаю не работает

Итак тема
Статические конструкторы/деструкторы объектов С++ и С++/С-функции с атрибутом __attribute__((constructor)) __attribute__((destructor)) - КАК ЭТО РАБОТАЕТ В GCC
я работаю с GCC 4.6 но сведения актуально вроде бы для весей четвертой ветке которой уже несколько лет.
1. статические конструкторы/деструкторы это вызовы котрые создают и уничтожают глобальные объекты, тоесть не созданные в куче и на стеке. Причем особенность этих объектов такова что они должны быть созданы до входа в main() и уничтожены посе выхода из нее.
2. а также функции с атрибутом __attribute__((constructor)) __attribute__((destructor)) аналогично должны быть вызваны до и после исполненя main()
3. исходя из вышеописанных требований очевидно что должен сущесвовать какойто метод автоматически вызывать эти конструкторы и функции автоматически без явного кода написанного программистом.
4. Про то что такое вообще конструктры и тд я полагаю что читающие этот пост знают все и поэтому сразу перехожу к описанию как это GCC релизует.
5. Расмотрим пример:
C++:
Код
class A
{
private:
int a;
public:
A(int init_val) { a = init_val; }
~A() {};
}
A a_hinst(12); // статическое объявление объекта
{
private:
int a;
public:
A(int init_val) { a = init_val; }
~A() {};
}
A a_hinst(12); // статическое объявление объекта
C:
Код
int val;
void _init() {}
void init_func() __attribute__ ((constructor))
{
}
void deinit_func() __attribute__ ((destructor))
{
}
void _init() {}
void init_func() __attribute__ ((constructor))
{
}
void deinit_func() __attribute__ ((destructor))
{
}
6. теперь откомпилируем эти исходники в объектники и посмотрим содержание c утилиты objdump. Что кроме стандартных секций появились такие интересные секции:
.init_array
.fini_array
что это такое? при встрече c/с++ компилятора с глобальным объявлением обектов равно как функций с вышеопределенными атрибутами он проделывает следующую манипуляцию - создает две специальную секцию - .init_array .fini_array в которых помещает массив адресов. это адреса функций с атрибутом конструктора, для объектов чучуть сложнее - в массив кладется адрес специально сгенерированного кода который вызывает конструктруктор с нужными параметрами(как минимум это параметр this - указатель на адрес куска памяти отведенное для экземпляра объекта). это сделано так потому что массиве может быть толко указатели на функции типа void func(void), такое ограничение вызвано тем что ТО что будет вызывать функции из этого массива ничего не может знать о них, а тем более о параметрах которые можно былобы туда передать.
.fini_array для деструкторов функций с атрубутом деструктор, ситуация аналогичная но только сзади

7. А теперь шачинается шаманство

а кто собсно должен вызвать это добро на исполнеее? эта функция видимо по стандарту возложена на реализацию libc. существует станадртная функция в libc c именем __libc_init_array(), ее код и дергает адреса из этих специальных секций. в моем случае это NewLib вот ее код ./newlib/libc/misc/init.c :
Код
#include <sys/types.h>
#ifdef HAVE_INITFINI_ARRAY
/* These magic symbols are provided by the linker. */
extern void (*__preinit_array_start []) (void) __attribute__((weak));
extern void (*__preinit_array_end []) (void) __attribute__((weak));
extern void (*__init_array_start []) (void) __attribute__((weak));
extern void (*__init_array_end []) (void) __attribute__((weak));
extern void (*__fini_array_start []) (void) __attribute__((weak));
extern void (*__fini_array_end []) (void) __attribute__((weak));
extern void _init (void);
extern void _fini (void);
/* Iterate over all the init routines. */
void
__libc_init_array (void)
{
size_t count;
size_t i;
count = __preinit_array_end - __preinit_array_start;
for (i = 0; i < count; i++)
__preinit_array_start[i] ();
_init ();
count = __init_array_end - __init_array_start;
for (i = 0; i < count; i++)
__init_array_start[i] ();
}
/* Run all the cleanup routines. */
void
__libc_fini_array (void)
{
size_t count;
size_t i;
count = __fini_array_end - __fini_array_start;
for (i = count; i > 0; i--)
__fini_array_start[i-1] ();
_fini ();
}
#endif
#ifdef HAVE_INITFINI_ARRAY
/* These magic symbols are provided by the linker. */
extern void (*__preinit_array_start []) (void) __attribute__((weak));
extern void (*__preinit_array_end []) (void) __attribute__((weak));
extern void (*__init_array_start []) (void) __attribute__((weak));
extern void (*__init_array_end []) (void) __attribute__((weak));
extern void (*__fini_array_start []) (void) __attribute__((weak));
extern void (*__fini_array_end []) (void) __attribute__((weak));
extern void _init (void);
extern void _fini (void);
/* Iterate over all the init routines. */
void
__libc_init_array (void)
{
size_t count;
size_t i;
count = __preinit_array_end - __preinit_array_start;
for (i = 0; i < count; i++)
__preinit_array_start[i] ();
_init ();
count = __init_array_end - __init_array_start;
for (i = 0; i < count; i++)
__init_array_start[i] ();
}
/* Run all the cleanup routines. */
void
__libc_fini_array (void)
{
size_t count;
size_t i;
count = __fini_array_end - __fini_array_start;
for (i = count; i > 0; i--)
__fini_array_start[i-1] ();
_fini ();
}
#endif
из кода все понятно как работает.
8. теперь самое главное - та часть которой мы управляем и пишем ручками - скрип линкера, это заключительный элемент цепочки котрый требуется для реализации этого прекрасного метода. Буду полагать что если не писать то хотябы читать скрипты GNU ld читаещие умеют, поэтому распишем только то что нужно дописать до стандарного который обычно имеется.
а) нада объявить секции .init_array .fini_array
б) окружить их адресными счетчиками чтоб компилятор мог при компиляции вычислить реальные адреса и размер массивом указателей в этих секциях.
вот кусок скрипта:
Код
.text :
{
_text_start_ = .;
/* вызов статических конструкторов */
__preinit_array_start = .;
KEEP(*(.preinit_array*))
__preinit_array_end = .;
__init_array_start = .;
KEEP(*(.init_array*))
__init_array_end = .;
*(.text)
*(.text*)
*(.rodata)
*(.rodata*)
*(.glue_7)
*(.glue_7t)
*(.gnu*)
*(.gcc*)
__fini_array_start = .;
KEEP(*(.fini_array*))
__fini_array_end = .;
. = ALIGN(8);
_text_end_ = .; /* define a global symbol _etext just after the last code byte */
} >flash /* put all the above into FLASH */
{
_text_start_ = .;
/* вызов статических конструкторов */
__preinit_array_start = .;
KEEP(*(.preinit_array*))
__preinit_array_end = .;
__init_array_start = .;
KEEP(*(.init_array*))
__init_array_end = .;
*(.text)
*(.text*)
*(.rodata)
*(.rodata*)
*(.glue_7)
*(.glue_7t)
*(.gnu*)
*(.gcc*)
__fini_array_start = .;
KEEP(*(.fini_array*))
__fini_array_end = .;
. = ALIGN(8);
_text_end_ = .; /* define a global symbol _etext just after the last code byte */
} >flash /* put all the above into FLASH */
9. И последний мазок кистью - в crt коде перед вызовом main добавить вызов __libc_init_array() а после нее __libc_fini_array. ФИСЕ.
теперь с точки прикладного программиста конструкторы будут вызыватся сами волшебным образом автоматически ровно как и деструкторы

Код
void Reset_Handler(void) // вызывается при сбросе проца
{
crt_init();
__libc_init_array();
main();
__libc_fini_array();
}
{
crt_init();
__libc_init_array();
main();
__libc_fini_array();
}
10. ну и наконец про то что мы всетаки эмбэдеры - все что относится к __libc_fini_array - можно смело выкинуть из скрипта линкера потому что мы эмбедеры и наша жизнь бесконечна в отличии от других яйцеголовых - наш main если не болен никогда некончается! покрайней мере пока есть элекричество в батарейке, это в частности приведет также к тому что с удалением секций из скрипта линкер выкинет из выходного файла и ненужный код деструкторов.
Итак, для прораммиста небходимо сделать модификацию crt кода и скрипта линкера, остальное сделает компиллер.
ps. я думаю что на русском языке подробнее об этом Вы непрочитаете, также надеюсь что мой труд не содержит банальных сведений и будет полезен. Я специально полопатил примеры FreeRTOS, демок к платкам Olimex. STM32 Primer и тд, нигде тема не раскрыта - там конструктры не работают
