0

I noticed that constructors from shared objects linked into my applications will always run before my application constructors, even if my applications has lower priority number (ie: higher priority); for example, let's say this program:

#include <stdio.h>

static void __attribute__ ((constructor (101))) test() {
    printf("test\n");
}

int main(int argc, char *argv[]) {
    return 0;
}

is linking the following shared object:

#include <stdio.h>

static void __attribute__ ((constructor (102))) test_so() {
    printf("test so\n");
}

I expected the output to be:

test
test so

Instead, the output is the opposite.
Is there any reason why? I could not find any documentation.

Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128
Federico
  • 221
  • 3
  • 11
  • What OS and linker? – Nate Eldredge Dec 01 '21 at 20:29
  • 1
    If I had a guess it would be C++, because it's valid to call static objects in a shared library in the `main` function or in a static constructor in the main program. So those constructors would have to run prior to the ones in the main program. – Mgetz Dec 01 '21 at 20:30
  • Priorities of this attribute only matter for the same program (.so in the fact is a separate program) and are not related between different units. – 0___________ Dec 01 '21 at 20:33
  • @NateEldredge it is an GCC extension – 0___________ Dec 01 '21 at 20:34
  • > Priorities of this attribute only matter for the same program (.so in the fact is a separate program) and are not related between different units. That's interesting; can you point me to a documentation about this? Or is this something obvious but not stated anywhere? – Federico Dec 01 '21 at 20:58
  • @NateEldredge archlinux, glibc 2.33-5 and gcc version 11.1.0 (GCC) – Federico Dec 01 '21 at 21:01
  • 2
    `can you point me to a documentation about this?` It's not like in open-source projects everything is documented, especially about extensions. Source code _is_ documentation. Here I think: https://github.com/lattera/glibc/blob/895ef79e04a953cac1493863bcae29ad85657ee1/elf/dl-init.c#L109 – KamilCuk Dec 01 '21 at 21:06
  • @Federico see how it works (debugging the startup code **before**). See how so libraries are initialized. It is not C standard. – 0___________ Dec 01 '21 at 21:10
  • @KamilCuk wow thank you very much! That seems to be the actual documentation for the behavior! – Federico Dec 01 '21 at 21:14

1 Answers1

2

Is there any reason why?

Facts:

  • __attribute__((__constructor__)) basically adds a pointer to the function to some ELF section
  • Every ELF file has a separate DT_INIT or DT_INIT_ARRAY sections.
  • Every ELF file is loaded in order, and dependencies are loaded before executable.
  • So shared libraries constructors will run before executable.
  • The ordering of __attribute__((__constructor__(this_number))) is limited to one ELF file, as compiler can reorder them there.

could not find any documentation.

It is documented in https://refspecs.linuxfoundation.org/elf/elf.pdf :

Initialization and Termination Functions

After the dynamic linker has built the process image and performed the relocations, each shared object gets the opportunity to execute some initialization code. All shared object initializations happen before the executable file gains control.

KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • The generic (draft) SysV spec can be found at http://www.sco.com/developers/gabi/latest/ch5.dynamic.html#init_fini NB (again): "All shared object initializations happen before the executable file gains control." – Andrew Henle Dec 01 '21 at 23:06