15

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

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

我们有一个模块(我称之为weird_module.so),它导出全局符号,其中之一是weird_module_function. 如果奇怪的模块.so 与特定的库(我将调用它libsomething.dylib)链接,则weird_module_function无法绑定。但是如果我删除-lsomethingwhen 链接weird_module.so,那么我可以绑定到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_LOCALRTLD_LAZY | RTLD_GLOBAL

其次,事实证明在全局中存在符号冲突weird_module.solibsomething.dylibconst

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

$ nm libsomething.dylib | grep '_something_global'
0000000000031130 S _something_global

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

4

1 回答 1

7

我试图重现您的场景,并且能够得到与您相同的错误,dyld: lazy symbol binding faileddyld: Symbol not found.

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

//  other_module.c

#import <stdio.h>
#import "weird_module.h"

__attribute__((constructor)) void initialize_other_module(void)
{
    printf("%s\n", __PRETTY_FUNCTION__);
    weird_module_function();
}

这是我加载模块的方式:

//  main.c

#import <stdio.h>
#import <dlfcn.h>

int main(int argc, const char * argv[])
{
    printf("\nLoading weird module\n");
    void *weird = dlopen("weird_module.so", RTLD_LAZY | RTLD_LOCAL);
    printf("weird: %p\n\n", weird);

    printf("Loading other module\n");
    void *other = dlopen("other_module.so", RTLD_LAZY | RTLD_LOCAL);
    printf("other: %p\n", other);

    return 0;
}

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

如果您weird_module_functionlibsomething.dylib构造函数调用,也会发生同样的错误,但它发生在main被调用之前,所以这可能不是发生在您身上的事情。

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

其他一些需要检查的事情:

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