C语言 OS X 上动态符号绑定的细节是什么



我在OS X上有一个非常奇怪的动态符号绑定情况,我希望得到一些有关如何解决的线索。

我有一个用 C 编写的应用程序,它使用 dlopen() 在运行时动态加载模块。其中一些模块导出全局符号,稍后加载的其他模块可能会使用这些符号。

我们有一个模块(我称之为weird_module.so)导出全局符号,其中一个是weird_module_function。如果 weird_module.so 与特定的库(我称之为 libsomething.dylib)链接,则weird_module_function不能绑定到。但是如果我在链接weird_module.so时删除-lsomething,那么我可以绑定到weird_module_function

libsomething.dylib可能导致weird_module.so不导出符号会发生什么?我可以做些什么来调试符号的导出方式(类似于如何使用DYLD_PRINT_BINDINGS来调试它们的绑定方式)?

$ LDFLAGS="-bundle -mmacosx-version-min=10.6 -Xlinker -undefined -Xlinker dynamic_lookup /usr/lib/bundle1.o"
$ gcc -o weird_module.so ${LDFLAGS} weird_module.o -lsomething
$ nm weird_module.so | grep '_weird_module_function$'
00000000000026d0 T _weird_module_function
$ gcc -o other_module.so ${LDFLAGS} other_module.o -lsomething
$ nm other_module.so | grep '_weird_module_function$'
                 U _weird_module_function
$ run-app
Loading weird_module.so
Loading other_module.so
dyld: lazy symbol binding failed: Symbol not found: _weird_module_function
  Referenced from: other_module.so
  Expected in: flat namespace
dyld: Symbol not found: _weird_module_function
  Referenced from: other_module.so
  Expected in: flat namespace
# Now relink without -lsomething
$ gcc -o weird_module.so ${LDFLAGS} weird_module.o
$ nm weird_module.so | grep '_weird_module_function$'
00000000000026d0 T _weird_module_function
$ run-app
Loading weird_module.so
Loading other_module.so
# No error!

编辑

我尝试将一个最小的应用程序放在一起来复制问题,在此过程中至少弄清楚了我们做错的一件事。还有另外两个与重复问题相关的事实。

首先,run-app预加载模块RTLD_LAZY | RTLD_LOCAL以检查其元数据。然后dlclose()模块,并根据元数据使用 RTLD_LAZY | RTLD_GLOBALRTLD_NOW | RTLD_LOCAL 重新打开。(对于有问题的两个模块,它以 RTLD_LAZY | RTLD_GLOBAL 重新打开)。

其次,事实证明,对于一个const全局来说,weird_module.solibsomething.dylib存在符号冲突。

$ nm weird_module.so | grep '_something_global`
00000000000158f0 S _something_global
$ nm libsomething.dylib | grep '_something_global'
0000000000031130 S _something_global

我愿意考虑重复符号会让我处于未定义行为的领域,所以我放弃了这个问题。

我试图重现您的场景,并且能够得到与您相同的错误,即 dyld: lazy symbol binding failed紧随其后的是dyld: Symbol not found

但这与是否与libsomething.dylib挂钩无关。我为触发此错误所做的只是从 other_module.so 的构造函数调用weird_module_function()

//  other_module.c
#import <stdio.h>
#import "weird_module.h"
__attribute__((constructor)) void initialize_other_module(void)
{
    printf("%sn", __PRETTY_FUNCTION__);
    weird_module_function();
}

以下是我加载模块的方式:

//  main.c
#import <stdio.h>
#import <dlfcn.h>
int main(int argc, const char * argv[])
{
    printf("nLoading weird modulen");
    void *weird = dlopen("weird_module.so", RTLD_LAZY | RTLD_LOCAL);
    printf("weird: %pnn", weird);
    printf("Loading other modulen");
    void *other = dlopen("other_module.so", RTLD_LAZY | RTLD_LOCAL);
    printf("other: %pn", other);
    return 0;
}

如果我在加载weird_module.so时删除RTLD_LOCAL选项,dyld 错误就会消失。

如果从 libsomething.dylib 构造函数调用 weird_module_function,也会发生相同的错误,但它发生在调用main之前,因此这可能不是发生在您身上的情况。

但也许libsomething.dylib构造函数是您应该查找libsomething.dylib如何影响模块加载过程的地方。您可以将DYLD_PRINT_INITIALIZERS环境变量设置为 YES,以便找出构造函数的名称。

要检查的其他一些事项:

  1. 您是否 100% 确定两个模块都使用 RTLD_LAZY | RTLD_GLOBAL 重新打开?我可以获得 dyld 错误的唯一方法是传递 RTLD_LOCAL 选项。
  2. 是否确定dlclose调用成功(返回 0)?例如,如果你的模块包含Objective-C代码,它不会被卸载。

最新更新