8

Let's say I have an libA.so with GCC constructor.

My program "program" depends on libA.so, so when I run it, libA.so gets opened and its constructor is executed. Now, I also have a module, libC.so, which also depends on libA. I run dlopen("libC.so"), which loads libC, and according to my experiments, also executes libA's constructor.

Dependencies look like this:

  • libA has the constructor
  • libB also has a constructor
  • libC depends on libA and libB
  • program depends on libA
  • program links libC via dlopen()

Now, when I run the program:

  • libA's constructor gets run before main() starts
  • libB's constructor gets run by dlopen()

Apparently, dlopen executes constructors of libraries when they are loaded into memory. Is this specified somewhere, and how does the linker check which libs are already loaded?

(Why I'm asking: On an occassion, I got a constructor executed twice in situation under some not-fully-understood conditions. Am I right in presuming that this was totally broken and should never happen under normal situation?)

che
  • 12,097
  • 7
  • 42
  • 71
  • 1
    Well, that's famously un/underspecified. How often can you load a library? Can you unload a library? Are two separate dependencies on shared libraries separate or the same? Questions over questions. – Kerrek SB Jan 09 '14 at 14:47
  • 1
    Post your code snippets, it's somewhat hard to understand what you're trying to explain here. – atx Jan 09 '14 at 15:08
  • 2
    Module constructor are called only once, unless you manualy unload the module, which is very unlikely. – ichramm Jan 09 '14 at 15:30
  • Using which platform you observe this behaviour? – alk Jan 09 '14 at 15:34
  • There seems to be something wrong in here: "*I also have a module, libB.so, which also depends on libA. I run dlopen("libA.so"), which loads libB, ...*" – alk Jan 09 '14 at 15:40
  • @alk Yeah...confusing. – atx Jan 09 '14 at 16:08
  • I got confused by the original code; when I tried to reproduce the issue in simplified form it has disappeared; I have updated the question. – che Jan 09 '14 at 16:51
  • As @alk asked, can you tell us about your platform? Are you using ELF files? – ninjalj Jan 09 '14 at 18:42
  • @ninjalj: The weird behavior has shown up on armv5tel with ELF, but I was not able to reproduce it on a clean example. – che Jan 10 '14 at 08:10

1 Answers1

7

For ELF files, __attribute__((constructor)) marked functions are ultimately run from the DT_INIT (initialization function) tag. Likewise for __attribute((destructor)) and the DT_FINI (termination function) tag.

Initialization functions are run after relocations are performed, and before returning control to the program, which for program-load-time loaded shared objects means before tranfering control to main(), and for runtime loaded shared objects could probably be interpreted as before returning from dlopen(). Initialization functions from DT_NEEDED shared objects are run before the initialization function in the current shared object.

Termination functions are run from atexit() handlers, after user atexit() handlers. Termination functions are run in reverse order of their corresponding initialization functions.

The ELF standard notes the following:

The dynamic linker ensures that it will not execute any initialization or termination functions more than once.

If you are seeing otherwise, then it's a bug.

ninjalj
  • 42,493
  • 9
  • 106
  • 148