OS X 上的动态符号绑定细节是什么?

15
我在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重新打开)。

其次,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

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

1个回答

7
我尝试重现你的场景,并且能够得到和你一样的错误,即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("%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;
}

如果我在加载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代码,则它将无法卸载。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接