问题
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
.
回答1:
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.
回答2:
By default, GCC's linker will only link in your static library (liblibrary.a
) if your program actually references some symbol from it.
Just using the library
So one way to make your library get linked in would be to use a symbol from it. For example, you could add this to main.cc
:
void myfunc();
...
std::cout << (void *)&myfunc << std::endl;
Or you could just manually call some initialization function in the library. At the point where you doing that, there is probably no reason to use __attr__((constructor))
any more.
Adding a linker option
Alternatively, you might try using the -Wl,--whole-archive
option to the linker as described here. To do that, you would add this line to src/Makefile.am:
hello_LDFLAGS = -Wl,--whole-archive
However, that caused my version of GCC to output tons of multiple definition errors for various symbols in libgcc.a
, so I don't know if that is a real solution.
回答3:
I ended up using -u link option and include driver init code from archives for drivers that I actually need. It seems reasonable since it's also a nice way to build everything and then control exactly what goes in the final program. I like this way a lot because I no longer need to supervise the compilation step with what gets included. I can compile and archive everything.
so when you link:
gcc -Wl,-u,myconstructor1,-u,myconstructor2 -o prog ... -llib1 -llib2
The generation of the list of constructors that you need can be automated based on selected features of the application. Although I have yet to figure out how to automate this using autotools.
来源:https://stackoverflow.com/questions/28652307/how-do-i-get-gnu-attribute-constructor-to-work-in-a-library