What operations are unsafe before __libc_init_array is invoked?

597 views Asked by At

I want to run some code before main begins, and before constructors for static variables run. I can do with with code like this (ideone)

extern "C" {
    static void do_my_pre_init(void) {
        // something
    }
    __attribute__ ((section (".preinit_array"))) void(*p_init)(void) = &do_my_pre_init;
}

Are there any language features that will not work correctly when executed in this function, due to _init and .init_array not yet having been executed?

Or is it only user code that should be hooking into this mechanism?


Some background on __libc_init_array

The source for a typical __libc_init_array is something like:

static void __libc_init_array() {
    size_t count, i;

    count = __preinit_array_end - __preinit_array_start;
    for (i = 0; i < count; i++)
        __preinit_array_start[i]();

    _init();

    count = __init_array_end - __init_array_start;
    for (i = 0; i < count; i++)
        __init_array_start[i]();
}

Where the __... symbols come from a linker script containing

. = ALIGN(4);
__preinit_array_start = .;
KEEP (*(.preinit_array))
__preinit_array_end = .;

. = ALIGN(4);
__init_array_start = .;
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
__init_array_end = .;
1

There are 1 answers

1
Employed Russian On

Are there any language features that will not work correctly when executed in this function, due to _init and .init_array not yet having been executed?

This question is impossible to answer in general, because the language itself has no concept of .preinit_array, or _init, or .init_array. All of these concepts are implementation details for a particular system.

In reality, you aren't guaranteed to have anything work at all. Things as simple as malloc may not work (e.g. because the malloc subsystem itself may be using .preinit_array to initialize itself).

In practice, using dynamic linking on a GLIBC-based platform most everything will work (because libc.so.6 initializes itself long before the first instruction of the main executable runs).

For fully-static executable, all bets are off.

For non-GLIBC platform, you'll need to look into specifics of that platform (and you are very unlikely to find any guarantees).

Update:

Can I make function calls,

Function calls need no setup with fully-static linking, and need dynamic loader to have initialized in dynamic linking case. No dynamic loader will start executing code in the application before it has fully initialized itself, so function calls should be safe.

assign structs

In C, at best, this is a few instructions. At worst, this is a call to memcpy or memset. That should be safe.

use array initializers.

This is just a special case of struct assignment, so should be safe.