Search code examples
c++linuxmakefilegnuautotools

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


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.


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.