8
votes

I have some simple declarations of a global instances with non-empty constructors. These constructors are called during startup automatically. I am cross-compiling C++ on Linux to different microcontroller targets.


As for

  • arm-none-eabi-gcc-4.8.4
  • rx-elf-gcc-4.8-GNURX_v14.03 (GCC 4.8.3)

calls to constructors are put into the .init_array section. The map file looks like this:

.init_array 0x00007cb8 0x4 libmotor.o

.init_array 0x00007cbc 0x4 libaudio.o


As for

  • mips-elf-gcc-4.8.2
  • avr-gcc-4.8.1
  • msp430-gcc-4.6.3

these calls get into the .ctors section:

.ctors 0x000000009d011508 0x4 libmotor.o

.ctors 0x000000009d01150c 0x4 libaudio.o


Compiling was done with -ffunction-sections -fdata-sections, the linker got --gc-sections.

All binaries work, but I'd like to put all calls into the same section (to simplify maintenance of linker scripts).

  • Why are there different target sections?
  • Is it possible to change the default section using a command line option?
  • If a command line option does not exit: Is it possible to define the default section at GCC compile-time?
1
I know you can gather many functions into a section, don't know how you can do that with constructors. Is it an option to create functions that create the instances (sort of like how Singletons work), so that you have all those functions in a section, and not care where the constuctors are?bolov
@bolov Actually I have already tried that (a function returning a reference to a function-local static instance), but that consumes too many flash space on small devices: Each call to that function requires some flash. But yes, I am exactly working on the problem of initialization order. One step towards this is the understanding of the different sections. Currently the different targets interpret the init_priority attribute in a different way (if at all), but that's a follow-up question.Beryllium
You cannot take the address of a constructor in C++ (it is illegal by the standard), so my wild guess is that you cannot explicitly specify to the linker in which section to put a constructor (unless the linker has something specially designed for constructors). But this is just a wild guess based on my very short experience with linker scripts. The way I know is that in order to gather many functions in a section and call them all you need the addresses of those functions.bolov
@DKrueger Yes, I have found that as well, but gave up last night to write an excerpt ... a really long story ... I am going to compare those statements with my findingsBeryllium

1 Answers

7
votes

There is a long discussion in Bug 46770 - Replace .ctors/.dtors with .init_array/.fini_array on targets supporting them

I have extracted some items which explain the situation:

Why did .init_array turn up?

  • We added .init_array/.fini_array in order to blend the SVR4 version of .init, which contained actual code, with the HP-UX version, which contained function pointers and used a DT_INIT_SZ entry in the dynamic array rather than prologue and epilogue pieces contributed from crt*.o files. The HP-UX version was seen as an improvement, but it wasn't compatible, so we renamed the sections and the dynamic table entries so that the two versions could live side-by-side and implementations could transition slowly from one to the other.

  • On HP-UX, we used .init/.init_array for static constructors, and they registered the corresponding static destructors on a special atexit list, rather than adding destructors to .fini_array, so that we could handle destructors on dlclose() events properly (subject to your interpretation of "properly" in that context)

The order of execution differs between .ctors and .init_array

Backwarding order of .ctors section

Some programs may implicitly rely on the fact that global constructors in archives linked later are run before constructors in the object linked against those archives. That is, given

g++ foo.o -lbar

where bar is a static archive, not a shared library, then currently the global constructors in objects pulled in from libbar.c will be executed before the global constructors in foo.o. That was an intentional choice because it is more likely to be correct than the reverse. However, the C++ standard does not guarantee it, so any programs which relies on this ordering is technically invalid.

Problem of backwarding order of .ctors

A lot of work was done in both GNU ld and gold to move constructors from .ctors to .init_array, all to improve startup latency for Firefox

Using .init_array/.fini_array instead of .ctors/.dtors removes the need for the associated (relative) relocations, and avoids the backwards disk seeks on startup (since while .ctors are processed backwards, .init_array is processed forward).

Transition from .ctors to .init_array

The mainline versions of both GNU ld and gold now put .ctors sections into .init_array sections, and put .dtors sections into .fini_array sections.

Comment: Probably introduced with GCC 4.7.

ARM

ARM EABI has been using .init_array from day one.

Comment: Nevertheless the default linker script contains a .ctors output section.

GCC configuration

One option you have is to configure gcc with --disable-initfini-array.

Comment: This option does not turn up in the output of mips-elf-gcc -v (-v shows "Configured with: ...").