Syntactic Confectionery Delight | |
PerlMonks |
[OT] Abusing GNU compiler and linker: Make the linker generate an array of function pointersby afoken (Chancellor) |
on Oct 16, 2019 at 19:48 UTC ( [id://11107560]=perlquestion: print w/replies, xml ) | Need Help?? |
afoken has asked for the wisdom of the Perl Monks concerning the following question: Hi! I painted byself into a corner at work. Environment: Atmelstudio 7, GCC 6.3.1 generating ARM code for running on bare metal. The basic problemMost of our C modules have some init function, many modules also have a run function. Essentially, the ARM does a poor man's cooperative multitasking, calling all run functions in a fixed order after having called all init functions. So we end up with code very similar to this:
Now guess what happens: You forget to add your New_Init() and New_Run() to main(), and suddenly, the system behaves very strange. In fact, this is number one on our debugging check list. Number two is wrong order of the init functions. Also, all that hand-written stuff takes a lot of time when you set up a new project. Using arraysSo I had the idea to delegate the job to the compiler and the linker. By convention, most init and run functions return void and don't take arguments. I made that a strict requirement, so now I have a common prototype for all init and run functions. I can manually fill two arrays with all of that functions, and reduce main() to iterate over the arrays:
Nothing gained so far, we would still have to add New_Init() and New_Run() to main.c. The only difference is that they go into the array initializers instead of main(). Now if I add some small macros to our modules, they take care of handling dependencies. Basically, some auto-generated flag variables and repeatedly calling all init functions until all are done. Init functions return immediately if their dependencies are not yet met. When an init function is done, it replaces itself with the module's run function. So I only have a single function pointer array, initially containing the init functions, later containing the run functions. Making the computer do itAll I need is an array automatically filled with function pointers to the init functions. Can't be that hard, right? Create a new section ".ourinits" in the linker script for the relocated (initialized) RAM area, create one symbols at start of the section and another one at end of the section, and make the macros generate an array fragment static Func_t XXX_addToList __attribute__((section(".ourinits"))) = XXX_Init;. Well, better don't make it static, because it looks unused to the compiler and will be removed; make it extern so that it reaches the linker. All of those manual work in main() is gone, main just has to iterate over an external array generated by macros and the compiler, and assembled by the linker. Except that it does not work. The linker decides to remove my array fragments. Of course it does, because no one references them by name. main() only accesses the array via the start and end symbols. D'oh! Making the computer do it, take two__attribute__((constructor))(That's where I stole a part of my plan.) Well, not really. Yes, it should generate a list of "constructor" functions, but they are called before main(), the list is in ROM so I can't change it, and I don't want the C runtime to call the init functions once. I want them to be called from main(), repeatedly, and until their dependencies have resolved. Also, I don't want to fight with the starting C runtime and 3rd party code using that mechanism. Help!My plan looks so good to us. All that I need is a way to tell the linker to stop cleaning up my precious section. Shouldn't be that hard, right? Alexander
-- Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
Back to
Seekers of Perl Wisdom
|
|