How do I get GNU __attribute__((constructor)) to work in a library?

autotools, c++, gnu, linux, makefile

Solution

I guess you have a Linux system. then ensure that the library is built as a shared library (see here), not a static one.

Functions with `__attribute__(constructor)` will be called when that shared library is loaded, e.g. at `ld.so` time, or at `dlopen` time if the library is a loaded plugin.

BTW the `__attribute__(constructor)` is more useful in C than in C++. In C++ you don't really need it, since you could have `static` data with some explicitly defined constructor in its `class` to achieve the same result.

For details, read Drepper's paper: How to Write a Shared Library.

Problem

I can get GNU `__attribute__((constructor))` to work (for a C++ program) if I link all object files together in a single link, but it doesn't work anymore if I store the object file containing the constructor function in a library and then link the library rather than the object file. What am I doing wrong? Makefile.am: ``` SUBDIRS = src ``` src/Makefile.am: ``` bin_PROGRAMS = hello hello_SOURCES = hello.cc register.cc register.hh myfunc.cc ``` src/hello.cc: ``` #include <iostream> // for cout #include <map> #include "register.hh" int main(int argc, char* argv[]) { std::cout << "Hello, World!" << std::endl; std::cout << "Have " << functions.size() << " functions registered." << std::endl; for (Function_map::iterator it = functions.begin(); it != functions.end(); ++it) { std::cout << "Registered " << (*it).first << std::endl; (*it).second(); } return 0; } ``` src/register.cc: ``` #include <map> #include <string> #include "register.hh" Function_map functions; void register_function(const std::string& name, Function f) { functions[name] = f; } ``` src/register.hh: ``` #ifndef REGISTER_H_ #define REGISTER_H_ #include <map> #include <string> typedef void (*Function)(); typedef std::map<const std::string, Function> Function_map; extern Function_map functions; void register_function(const std::string& name, Function f); #endif ``` src/myfunc.cc: ``` #include "register.hh" #include <iostream> void myfunc() { std::cout << "This is myfunc!" << std::endl; } __attribute__((constructor)) void register_myfunc() { register_function("MYFUNC", myfunc); } ``` configure.ac: ``` AC_PREREQ([2.69]) AC_INIT([hello], [1.4], [bugs@my.domain]) AC_CONFIG_SRCDIR([src/hello.cc]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_AUX_DIR([auxiliary]) AM_INIT_AUTOMAKE([-Wall -Werror]) AC_PROG_CXX AM_PROG_AR AC_CONFIG_FILES([Makefile src/Makefile]) AC_OUTPUT ``` so all C++ files are compiled into object files which get linked together into the 'hello' executable. The output from the resulting 'hello' program is: ``` Hello, World! Have 1 functions registered. Registered MYFUNC This is myfunc! ``` If I change src/Makefile.am to ``` bin_PROGRAMS = hello hello_SOURCES = hello.cc register.cc register.hh hello_LDADD = liblibrary.a noinst_LIBRARIES = liblibrary.a liblibrary_a_SOURCES = myfunc.cc ``` (i.e., myfunc.cc compiles into myfunc.o which is stored in liblibrary.a which gets linked with the other object files into 'hello'), then the output from 'hello' is ``` Hello, World! Have 0 functions registered. ``` so now the 'register_myfunc' function wasn't executed. Why not? EDITED 2015-02-22 (in response to the answer from Basile Starynkevitch): I'm using a GNU/Linux (Fedora 20) system. I tried building a shared library using libtools, but without success. I adjusted the src/Makefile.am as follows: ``` bin_PROGRAMS = hello hello_SOURCES = hello.cc register.cc register.hh hello_LDADD = liblibrary.la noinst_LTLIBRARIES = liblibrary.la liblibrary_la_SOURCES = myfunc.cc liblibrary_la_LDFLAGS = -shared -fPIC ``` (first with just `-shared`, later with `-fPIC` too) and added `LT_INIT` to configure.ac, but that did not change the outcome. I'll try the "static data with explicit constructor" trick that you mention for C++, but am still interested to know how to get my example to work with `__attribute__((constructor))`. EDITED 2015-02-23 I tried the "static data with explicit constructor" trick but got the same results as before: it works if all objects files are linked together explicitly into an executable, but not if the thing that I want constructed automatically is linked into the executable through a library. Adding `hello_LDFLAGS = -Wl,--whole-archive` (suggested by David Grayson) leads to many "multiple definition" errors. Automake places those flags near the beginning of the link command so it applies not just to the library. Automake recommends against including linker flags directly in `hello_LDADD` where the library to link with is specified. It is possible to override Automake rules with explicit Make rules (where I can put the linker flags exactly where I want them), but then I may run the risk of other standard Make rules (supplied by Automake) misbehaving. I'll see if I can get it to work using `dlopen`.

Original source

Related problems