Как создать реестр функций низкого уровня
Сначала вы создаете макрос для размещения указателей на ваши функции в специальном разделе:
/* original typedef from question: */
typedef int16_t (*myfunc)(void);
#define myfunc_register(N) \
static myfunc registered_##myfunc_##N \
__attribute__((__section__(".myfunc_registry"))) = myfunc_##N
Имя статической переменной является произвольным (оно никогда не будет использоваться), но приятно выбрать выразительное имя. Вы используете его, разместив регистрацию чуть ниже своей функции:
myfunc_register(NUMBER);
Теперь, когда вы компилируете свой файл (каждый раз), он будет иметь указатель на вашу функцию в разделе .myfunc_registry
. Все это будет скомпилировано как есть, но без скрипта компоновщика ничего хорошего не получится. Спасибо caf за указание на относительно новую функцию INSERT AFTER
:
SECTIONS
{
.rel.rodata.myfunc_registry : {
PROVIDE(myfunc_registry_start = .);
*(.myfunc_registry)
PROVIDE(myfunc_registry_end = .);
}
}
INSERT AFTER .text;
Самая сложная часть этой схемы - создание всего сценария компоновщика: вам нужно встроить этот фрагмент в фактический скрипт компоновщика для вашего хоста , который, вероятно, доступен только при сборке binutils вручную и проверка дерева компиляции или через strings ld
. Обидно, потому что мне очень нравятся трюки со скриптами компоновщика.
Связь с gcc -Wl,-Tlinkerscript.ld ...
Опция -T
улучшит (а не заменит) существующий скрипт компоновщика.
Теперь компоновщик соберет все ваши указатели с атрибутом section вместе и услужливо предоставит символ, указывающий до и после вашего списка:
extern myfunc myfunc_registry_start[], myfunc_registry_end[];
Теперь вы можете получить доступ к вашему массиву:
/* this cannot be static because it is not know at compile time */
size_t myfunc_registry_size = (myfunc_registry_end - myfunc_registry_start);
int i;
for (i = 0; i < myfunc_registry_size); ++i)
(*myfunc_registry_start[i])();
Они не будут в каком-то определенном порядке. Их можно пронумеровать, поместив их в __section__(".myfunc_registry." #N)
, а затем в сборщик компоновщиков *(.myfunc_registry.*)
, но сортировка будет лексографической, а не числовой.
Я проверил это с gcc 4.3.0 (хотя части gcc были доступны в течение долгого времени) и ld 2.18.50 (вам нужен довольно свежий ld для магии INSERT AFTER
).
Это очень похоже на то, как компилятор и компоновщик сговариваются для выполнения ваших глобальных ctors, так что было бы намного проще использовать статический конструктор класса C ++ для регистрации ваших функций и значительно более переносимый.
Вы можете найти примеры этого в ядре Linux, например, __initcall
очень похоже на это.