cancel
Showing results for 
Search instead for 
Did you mean: 

The mysterious __libc_init_array() in Cube IDE - is it only for C++ ??

PHolt.1
Senior III

Supposedly this is only used by C++ but see various threads e.g.

https://www.eevblog.com/forum/microcontrollers/a-question-on-gcc-ld-linker-script-syntax/msg4754165/#msg4754165

https://www.eevblog.com/forum/microcontrollers/how-do-i-enable-link-section-overlap/msg3715957/#msg3715957

https://www.eevblog.com/forum/microcontrollers/mcu-programming-101-writeup/msg5402906/#msg5402906

https://www.eevblog.com/forum/microcontrollers/is-st-cube-ide-a-piece-of-buggy-***/msg5403485/#msg5403485

If you google it, you find a disturbing number of suggestions (nothing concrete!) that in the STM environment, Cube IDE, this function is not only for C++ constructors etc but is also used to initialise some statics in libc.a functions. One suggestion was that malloc() does not work without it. ST does not distribute sources to libc.a although some people on eevblog located probable sources for newlib (dated 1990, IIRC). Much of libc.a is also not re-entrant and needs to be mutexed in any RTOS environment.

Does anyone know the real answer?

21 REPLIES 21
PHolt.1
Senior III

That is a circular example, however. 

If you want to run some code before main() you just insert it in the startup.s code which calls main() at the end.

I don't see the point of __libc_init_array(). You can call any C function from anywhere before main(), which is just an entry point of a void function like any other C function.

 

 

The keyword here is "magic".

It's the widespread expectation, that things should "happen" and the unfortunate urge of many influential people to provide the "magic", instead of - arguably harder, but arguably more appropriate - proper documentation/education. It's exactly the same principle as in IDEs and Cubes.

Here, the expectation is, that you should be able to run a function before main() without knowing what leads up to main, i.e. without knowledge of the startup code. Said atributes are the incantation to invoke the magic, __libc_init_array() is part of the set of mirrors which perform the magic.

JW

 

I'm not sure why this has devolved into this binary philosophical argument.

The mechanics here is to allow for constructor / new() and destructor / delete() functionality in LIBC more broadly. Yes, ideally everything in C won't use this, but that's a bit myopic, and code might be brought it that's not entirely pure.

Would it be a pain to review all the objects, in all the libraries? Sure, but it could be done and automated.

ARM wants you call SystemInit() in a pre-main() sense to get the MCU and related hardware, memories and peripherals to a point that the C/C++ run time can initialize the system properly and fully.

Once the hardware's up the statics need to be cleared or copied into place in RAM, and then anything with constructor expectations is brought up. The linker/script facilitates the construction of a call-table listing everything that need explicit initialization to operates as expected. The kitchen sink approach allows for whatever is built to actually work, without requiring everyone to understand every nuance of the pieces being brought together.

The GNU/GCC chains are aimed more general purpose executables in Linux, in the bare-metal / embedded sense we're using a subset of functionality with hard addresses, and systems that don't ever exit and return to a command prompt. The compiler is going to build destructors with the expectation they are going to happen but we don't handle that in startup.s, nor exit() / return conditions.

The staging of information in ROM to moving it to RAM is also somewhat alien to GNU/GCC, and ST's implementation is particularly weak/crude when compared to Arduino's or more embedded specific compilers from Keil and IAR, etc that have had to build ROM code for many decades, across many architectures.

 

If you don't want to use __libc_init_array(), then DON'T, but perhaps put in other cross-checks to catch situations where the linker and your entire build foot-print doesn't create a constructor call table, so it doesn't surprise you when the code doesn't initialize properly and fails. Should be a relatively trivial task to put a sanity check in main() that alerts you in some fashion that pre-deployment checks can catch. Perhaps have a post-link RULES CHECK in your build processes.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
PHolt.1
Senior III

Jan, I accept your tongue in cheek post 😁 but I must still be missing something big.

Why would anyone want to run a function before main() ? After all, main() is the entry point of your code. Nothing should be happening before main() - other than stuff to set up the language standards e.g. zero bss, copy init data from ROM to RAM, zero the stack (or fill with "s") for good measure, and of course load SP which is necessary before calling functions (of which main() is one). No application specific code should ever run before main().

I am talking "embedded" here, not the command line stuff whereby main() implicitly picks up argc argv arguments etc.

I know nothing about C++ but can fully understand some needs for setting up data structures.

If a function is needed to set up RAM static variables in C code (as has been alleged __libc_init_array() does, setting up stuff like stdlib printf malloc etc) why can't this go after main()? Only if you want to avoid documenting "why".

I posted some disassembled code and maybe someone has an idea what it does in C.

 

PHolt.1
Senior III

About half of my post above had gone missing.

I posted some disassembled code and maybe someone has an idea what it does in C.

I haven't fully decode the disassembly  but it *should* iterate thru array of pointers to functions, and calls each function.

With other questions you may have better luck on Stack Overflow. (caution: downvoters there...)

PHolt.1
Senior III

I posted the linkfile code which connects with __libc_init_array() but it was removed. Anyway, here it is as a graphic:

>>Why would anyone want to run a function before main() ?

So when you arrive there every thing is ready?

Why do you use globals? Why do you expect variables / arrays to have specific and non-zero values in them?

The startup code is there to get you from a reset micro-controller with very few guarantees about the start conditions of anything, especially RAM and externally attached hardware, to a viable C environment with things initialized as expected.

 

Microsoft C, be it the old DOS era stuff, or Win32 / 64 .EXE stuff, has "Runtime Environment Startup" code that gets you between the OS and your main() function. Initializing stacks, parsing the command line, providing the environment variables.

A lot of magic has to occur prior to main() for your world view of the system to function. Here you seem to be scared about the sausage gets made, and just want to eat the cooked sausages..

 

Sort of materials I'd expect would be covered in linker, loader, assemblers, compiler type foundational classes.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

One could size these, and flag a check or objection if that gets to be non-zero.

The compiler is designed to shovel things into these object/sections as it goes, along with epilogue / prologue code, literals, etc. The linkers designed to filter and collect these things from a stream of output.

It's going to bend your mind if you start using structured exception handling methods like try/catch/throw...

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

> Why?

Here is an example. You may find it ridiculous; those who want to experience magic, don't. That example doesn't mention another level of magic - supplying mentioned constructor as part of library, i.e. completely out of scope of the end user, who just enjoys the magical results.

> EXE stuff...

Sure, but we are talking micro*controllers*.

> Sort of materials I'd expect would be covered in linker, loader, assemblers, compiler type foundational classes.

I highly doubt they are in any relevant details.

Note that it's the academia which disseminated the idea that you are a hopeless loser if you don't use abstraction (as in HAL), encapsulation, information hiding, objects, modelling etc. It's the academia which perpetuates the notion of pure hardware and pure software engineers. Practicing academics are rare and they tend not to be popular types - no wonder, preaching blood, tears, toil and sweat will never attract masses.

> The kitchen sink approach allows for whatever is built to actually work, without requiring everyone to understand every nuance of the pieces being brought together.

That's OK. Problem is, that, apparently, at this point *nobody* does.

JW