如果我在单个链接中将所有目标文件链接在一起,则可以使GNU __attribute__((constructor))工作(对于C++程序),但是如果我将包含构造函数的目标文件存储在库中,然后再链接该库,它将不再起作用而不是目标文件。我究竟做错了什么?

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

因此,所有C++文件都被编译成目标文件,这些目标文件链接在一起成为“hello”可执行文件。

结果“hello”程序的输出为:
Hello, World!
Have 1 functions registered.
Registered MYFUNC
This is myfunc!

如果我将src/Makefile.am更改为
bin_PROGRAMS = hello
hello_SOURCES = hello.cc register.cc register.hh
hello_LDADD = liblibrary.a

noinst_LIBRARIES = liblibrary.a
liblibrary_a_SOURCES = myfunc.cc

(即myfunc.cc编译为myfunc.o,该文件存储在liblibrary.a中,该文件与其他目标文件链接到“hello”中),则“hello”的输出为
Hello, World!
Have 0 functions registered.

因此现在未执行'register_myfunc'函数。为什么不?

编辑日期2015-02-22 (响应Basile Starynkevitch的回答):我正在使用GNU/Linux(Fedora 20)系统。我尝试使用libtools构建共享库,但没有成功。我对src/Makefile.am进行了如下调整:
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

(首先只添加-shared,然后添加-fPIC),并添加LT_INIT到configure.ac,但这并没有改变结果。我将尝试使用C++提到的“带有显式构造函数的静态数据”技巧,但仍然有兴趣知道如何使我的示例与__attribute__((constructor))一起使用。

编辑于2015-02-23 我尝试了“使用显式构造函数的静态数据”的技巧,但获得了与以前相同的结果:如果所有对象文件都显式链接到可执行文件中,则可以使用,但是如果我要构造的东西则不能使用通过库自动链接到可执行文件。

添加hello_LDFLAGS = -Wl,--whole-archive(由David Grayson建议)会导致许多“多个定义”错误。 Automake将这些标志放在链接命令的开头附近,因此它不仅适用于库。 Automake建议不要在指定要链接的库的hello_LDADD中直接包含链接器标志。可以使用显式的Make规则(可以在其中将链接器标记准确放置在所需的位置)覆盖Automake规则,但是随后,我可能会冒其他行为不正常的标准Make规则(由Automake提供)的风险。

我将看看是否可以使用dlopen使它正常工作。

最佳答案

我猜你有一个Linux系统。然后确保将该库构建为共享库(请参见here),而不是静态库。

加载共享库时,将调用带有__attribute__(constructor)的函数,例如在ld.so时,或者在dlopen时(如果库是已加载的插件)。

顺便说一句,__attribute__(constructor)在C中比在C++中更有用。在C++中,您实际上并不需要它,因为您可以在static中包含带有明确定义的构造函数的class数据来达到相同的结果。

有关详细信息,请阅读Drepper's paper: How to Write a Shared Library

09-04 20:42
查看更多