Search code examples
c++gccinitializer

Is the init section functions called first or the constructor functions?


Consider the following code:

#include <iostream>
#include <stdio.h>

void preinit_void();
void init_void();

__attribute__((section(".init_array")))
    void (*p_init)(void) = &init_void;

void constructor_void() __attribute__((constructor));
void constructor_void() {
    printf(__FUNCTION__);
}

__attribute__((section(".preinit_array")))
    void (*p_preinit)(void) = &preinit_void;


void preinit_void() {
    printf(__FUNCTION__);
}

void init_void() {
    printf(__FUNCTION__);
}

int main() {
    std::cout << __FUNCTION__ << '\n';
}

On running the code the output is

preinit_voidinit_voidconstructor_voidmain

If I were to change the code to:

#include <iostream>
#include <stdio.h>

void preinit_void();
void init_void();

void constructor_void() __attribute__((constructor));
void constructor_void() {
    printf(__FUNCTION__);
}

__attribute__((section(".init_array")))
    void (*p_init)(void) = &init_void;

__attribute__((section(".preinit_array")))
    void (*p_preinit)(void) = &preinit_void;


void preinit_void() {
    printf(__FUNCTION__);
}

void init_void() {
    printf(__FUNCTION__);
}

int main() {
    std::cout << __FUNCTION__ << '\n';
}

The output changes to:

preinit_voidconstructor_voidinit_voidmain

I am confused as to which section is initialized first. Does the output change merely due to parsing by compiler (that it found .init_array section first instead of the constructor) or is there a proper initialization sequence for it?


Solution

  • On current (GNU) systems, references to ELF constructor functions are placed into the .init_array section, just like the entries you add manually. This why the executing order changed when you changed the source code order. GCC and binutils have a language extension for using separate sections based on priority, and the link editor will serialize everything into a final .init_array section for each application/shared object (based on source code and link order, after sorting by priority).

    The constructor arrays for different shared objects are executed after performing a topological sort based on the dependencies between objects (as expressed through the DT_NEEDED tag), so that objects are initialized after their dependencies.

    This goes way beyond what is required by C++ for initialization, but it is more or less specified in various (GNU) ABI-related documents, and it is not going to change. Some aspects might change due to the introduction of new features (e.g., multiple DF_1_INITFIRST objects, although this one is a bit baffling), but they will hopefully only affect processes which actually use those features.