How To Make a Low-Level Function Registry
First you create a macro to place pointers to your functions in a special section:
/* original typedef from question: */
typedef int16_t (*myfunc)(void);
#define myfunc_register(N)
static myfunc registered_##myfunc_##N
__attribute__((__section__(".myfunc_registry"))) = myfunc_##N
The static variable name is arbitrary (it will never be used) but it's nice to choose an expressive name. You use it by placing the registration just below your function:
myfunc_register(NUMBER);
Now when you compile your file (each time) it will have a pointer to your function in the section .myfunc_registry
. This will all compile as-is but it won't do you any good without a linker script. Thanks to caf for pointing out the relatively new INSERT AFTER
feature:
SECTIONS
{
.rel.rodata.myfunc_registry : {
PROVIDE(myfunc_registry_start = .);
*(.myfunc_registry)
PROVIDE(myfunc_registry_end = .);
}
}
INSERT AFTER .text;
The hardest part of this scheme is creating the entire linker script: You need to embed that snippet in the actual linker script for your host which is probably only available by building binutils by hand and examining the compile tree or via strings ld
. It's a shame because I quite like linker script tricks.
Link with gcc -Wl,-Tlinkerscript.ld ...
The -T
option will enhance (rather than replace) the existing linker script.
Now the linker will gather all of your pointers with the section attribute together and helpfully provide a symbol pointing before and after your list:
extern myfunc myfunc_registry_start[], myfunc_registry_end[];
Now you can access your array:
/* 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])();
They will not be in any particular order. You could number them by putting them in __section__(".myfunc_registry." #N)
and then in the linker gathering *(.myfunc_registry.*)
, but the sorting would be lexographic instead of numeric.
I have tested this out with gcc 4.3.0 (although the gcc parts have been available for a long time) and ld 2.18.50 (you need a fairly recent ld for the INSERT AFTER
magic).
This is very similar to the way the compiler and linker conspire to execute your global ctors, so it would be a whole lot easier to use a static C++ class constructor to register your functions and vastly more portable.
You can find examples of this in the Linux kernel, for example __initcall
is very similar to this.