10

In my shared library I have to do certain initialization at the load time. If I define the function with the GCC attribute __attribute__ ((constructor)) it doesn't work, i.e. it doesn't get called when the program linking my shared library is loaded.

If I change the function name to _init(), it works. Apparently the usage of _init() and _fini() functions are not recommended now.

Any idea why __attribute__ ((constructor)) wouldn't work? This is with Linux 2.6.9, gcc version 3.4.6

Edit:

For example, let's say the library code is this the following:

#include <stdio.h>

int smlib_count;

void __attribute__ ((constructor)) setup(void) {
    smlib_count = 100;
    printf("smlib_count starting at %d\n", smlib_count);
}

void smlib_count_incr() {
    smlib_count++;
    smlib_count++;
}

int smlib_count_get() {
    return smlib_count;
}

For building the .so I do the following:

gcc -fPIC -c smlib.c
ld -shared -soname libsmlib.so.1 -o libsmlib.so.1.0 -lc smlib.o
ldconfig -v -n .
ln -sf libsmlib.so.1 libsmlib.so

Since the .so is not in one of the standard locations I update the LD_LIBRARY_PATH and link the .so from a another program. The constructor doesn't get called. If I change it to _init(), it works.

Dan Fego
  • 13,644
  • 6
  • 48
  • 59
Manohar
  • 3,865
  • 11
  • 41
  • 56
  • It's relevant where in the function definition you place the attribute string. Where is it in yours? I have one that works like so: `void __attribute__ ((constructor)) constructor() { ... }`. Note that it's after `void` and before `constructor()`. – Dan Fego Feb 27 '12 at 22:06
  • 1
    Not an answer, but it's worth noting that shared library constructors are generally *Considered Harmful* due to their ability to mess up the initial state of the program before `main` is entered. The most infamous example is OpenAL, which (at least at one time; I believe it was since fixed but I'm not sure) munged with ALSA library/device state in a way that prevented the application from later opening the ALSA device itself. Proper libraries should try their best to avoid having any global state, and if absolutely necessary, should use lazy initialization on the first library call, not ctors. – R.. GitHub STOP HELPING ICE Feb 28 '12 at 00:29
  • @Dan Fego, I tried different placements including the one you suggested above. It still doesn't work. – Manohar Mar 09 '12 at 10:40
  • @R.. I understand the risks involved. But I am just going to initialize some variables specific to my library. – Manohar Mar 09 '12 at 10:41
  • @Santhosh Can you please post a minimal [example](http://sscce.org/) that exhibits the problem, and preferably your compilation lines and output and the way you're calling it? – Dan Fego Mar 09 '12 at 14:15
  • Do you perhaps have a function named _init or _fini in the library already? If so, these will conflict with the startup code and prevent running your constructors... – R.. GitHub STOP HELPING ICE Mar 09 '12 at 16:46
  • @Dan, Please see the sample code in my question.. its very basic. Have also given the commands I used.. may be I am missing something in the way .so should be built ? – Manohar Mar 09 '12 at 18:21
  • @Santhosh I'm not 100% sure why this is, but on a similarly old box (but not on a new one), I can reproduce your problem. If I change your `gcc` and `ld` steps to a single `gcc` step, however, it seems to go away. Care to try that out and report back? Mine was: `gcc -Wall -shared -fPIC -o libsmlib.so smlib.c`. – Dan Fego Mar 09 '12 at 19:00
  • @Dan you are right..If I build the so in one step then it works. The typical version numbering of the .so files is pretty confusing to me. Is it really required to have those .so.1 and .so.1.0 files ? Probably its required when you want to release it a bigger project and to incremental updates ? You said you see the problem in a old box, what is old on that, kernel version or gcc or something else ? And Thanks a lot for the help :) – Manohar Mar 09 '12 at 19:21
  • @Santhosh See my answer below for an explanation on why this is happening. As for the `.so.1` stuff, I don't think it's necessary -- just convention. – Dan Fego Mar 09 '12 at 19:25
  • Also see the discussion of constructors, `-Ur` and `-r` in the [`ld` man pages](https://linux.die.net/man/1/ld). `-init=name` may be useful, too because it sets `DT_INIT`. – jww Jan 11 '18 at 19:33

2 Answers2

4

Okay, so I've taken a look at this, and it looks like what's happening is that your intermediate gcc step (using -c) is causing the issue. Here's my interpretation of what I'm seeing.

When you compile as a .o with setup(), gcc just treats it as a normal function (since you're not compiling as a .so, so it doesn't care). Then, ld doesn't see any _init() or anything like a DT_INIT in the ELF's dynamic section, and assumes there's no constructors.

When you compile as a .o with _init(), gcc also treats it as a normal function. In fact, it looks to me like the object files are identical except for the names of the functions themselves! So once again, ld looks at the .o file, but this time sees a _init() function, which it knows it's looking for, and decides it's a constructor, and correspondingly creates a DT_INIT entry in the new .so.

Finally, if you do the compilation and linking in one step, like this:

gcc -Wall -shared -fPIC -o libsmlib.so smlib.c

Then what happens is that gcc sees and understands the __attribute__ ((constructor)) in the context of creating a shared object, and creates a DT_INIT entry accordingly.

Short version: use gcc to compile and link in one step. You can use -Wl (see the man page) for passing in extra options like -soname if required, like -Wl,-soname,libsmlib.so.1.

Dan Fego
  • 13,644
  • 6
  • 48
  • 59
  • 6
    It doesn't have to be in one step. What makes it work is using gcc with -shared to link, not ld. – Thomas Apr 13 '12 at 21:31
  • Is there no option to have constructor but with 2 steps (.o then .so)? It is necessary for CMake projects – dashesy Aug 09 '16 at 21:46
2

From this link :

"Shared libraries must not be compiled with the gcc arguments -nostartfiles'' or-nostdlib''. If those arguments are used, the constructor/destructor routines will not be executed (unless special measures are taken)."

gcc/ld doesn't set the DT_INIT bit in the elf header when -nostdlib is used . You can check objdump -p and look for the section INIT in both cases. In attribute ((constructor)) case you wont find that INIT section . But for __init case you will find INIT section in the shared library.

Lunar Mushrooms
  • 8,358
  • 18
  • 66
  • 88