加载共享库时自动执行的功能

时间:2012-03-18 16:16:09

标签: posix shared-libraries d dlopen

在Windows中加载共享库时,LoadLibrary()调用会导致库中的DllMain为每个新进程和线程库附加执行,并为每个进程和线程库detaaches执行。

Mac OS X,Linux和其他POSIX兼容的操作系统是否有类似的机制?

4 个答案:

答案 0 :(得分:51)

您可以使用.init机制为linux库定义一个on-load函数。这与指定二进制文件的加载时间入口点相同(例如,使用除main之外的其他东西作为程序的入口点)。

直接使用ld进行关联时,请使用:

-init <function name>

或者如果您使用cc / gcc进行链接,请使用:

-Wl,-init,<function name>

这是最简单的水平。

修改 对于析构函数/终结器,您使用.fini机制。其操作方式与init选项相同,您可以使用:

-fini <function name>

调用ld时。可用性仅限于Mac OSX平台上的-init选项。

您还应该能够使用gcc:{/ p>的__attribute__((constructor))语法

static void con() __attribute__((constructor));

void con() {
    printf("I'm a constructor\n");
}

这可能是一种更便携的方式,而不是使用链接器选项。应该在加载时调用所有构造函数,但依赖于它们的初始化顺序,这会导致精神错乱和不可重现的错误,这会花费时间和精力进行调试。

编辑2 使用__attribute__((constructor))/__attribute__((destructor))语义是C / C ++编程语言最优选的机制。

对于D编程语言,您应该使用静态模块构造函数/析构函数:

static this() {
    printf("static this for mymodule\n");
}
static ~this() {
    printf("static ~this for mymodule\n");
}

或静态类构造函数:

class Foo {
    static this() {
        printf("static this for Foo\n");
    }
}

writing win32 DLLS和语言规范relating to static constructors/destructors强烈暗示了这一点。

编辑3 您需要链接导出构造函数/析构函数例程的.o,这将允许使用静态初始值设定项。因为它应该做的就是调用Runtime.initialize(),这实际上会调用D代码中的所有静态构造函数/析构函数。

初始化程序的存储代码(在名为myshared.d的文件中):

import core.runtime;

extern (C) {
    void attach();
    void detach();
}

export void attach() {
    Runtime.initialize();
}

export void detach() {
    Runtime.terminate();
}

为此存根创建.o:

 dmd -m32 -c myshared.d

检查附加/分离功能的名称:

nm myshared.o

显示(以及其他输出):

0000001c S _D8myshared6attachFZv
00000034 S _D8myshared6detachFZv

用于调用此代码的示例.c代码(在本例中称为export.c),我们从my shared.o文件中引用导出例程的名称:

extern void D8myshared6attachFZv(void);
extern void D8myshared6detachFZv(void);

void __attach(void) __attribute__((constructor));
void __detach(void) __attribute__((destructor));

void __attach(void)
{
    D8myshared6attachFZv();
}

void __detach(void)
{
    D8myshared6detachFZv();
}

请注意,extern void引用需要使用导出函数的错位名称。这些必须匹配或代码不会链接。

使用以下代码编译C代码:

gcc -m32 -c export.c

使用以下方法将.c.o和.d.o文件链接在一起:

cc -o libmyshared.dylib -m32 -shared myshared.o export.o -lphobos2

假设phobos2库位于标准链接器搜索路径中。编译器和链接器的-m32选项的第一部分是因为我在本地构建的D编译器的版本仅支持32位。

这会产生一个可以链接的.dylib。它似乎是基于我执行的有限测试工作。看起来对共享对象/动态库的支持非常有限,因此很有可能需要克服另一个障碍。

答案 1 :(得分:11)

要在加载或卸载共享库时执行函数,可以使用特定于GCC的属性语法标记构造函数和析构函数:

__attribute__((constructor)) void init(void) { ... }
__attribute__((destructor))  void fini(void) { ... }

由于C环境的各个部分依赖于GCC在幕后添加的标准.init代码中初始化的内容,因此直接使用-Wl,-init,<function name>可能会导致程序崩溃。

有关详细信息,请参阅Library constructor and destructor functions上的Libary HOWTO。

答案 2 :(得分:6)

GCC以及clang AFAIK支持GCC构造函数和析构函数属性。有关详细信息,请参阅How exactly does __attribute__((constructor)) work?

答案 3 :(得分:0)

对于C ++,您可以创建一个类并使用他的构造函数和析构函数来初始化库。

之后,您只需要为此类定义一个变量。

在库中初始化openssl的示例:

class InitLibrary {
public:
  InitLibrary() {
    CRYPTO_malloc_init(); // Initialize malloc, free, etc for OpenSSL's use
    SSL_library_init(); // Initialize OpenSSL's SSL libraries
    SSL_load_error_strings(); // Load SSL error strings
    ERR_load_BIO_strings(); // Load BIO error strings
    OpenSSL_add_all_algorithms(); // Load all available encryption algorithms
  }

  ~InitLibrary() {
    ERR_remove_state(0);
    CRYPTO_cleanup_all_ex_data();
    ENGINE_cleanup();
  }
};

并且只在cpp文件中添加此行: InitLibrary InitLib;