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

时间:2013-09-09 15:33:27

标签: c macos dyld

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

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

我们有一个模块(我称之为weird_module.so),它导出全局符号,其中一个是weird_module_function。如果weird_module.so与特定库(我称之为libsomething.dylib)链接,则weird_module_function无法绑定。但是,如果我在关联-lsomething时删除了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_LOCAL重新打开,具体取决于元数据。 (对于这两个模块,它会重新打开RTLD_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

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

1 个答案:

答案 0 :(得分:7)

我尝试重现您的方案,我能够得到与您相同的错误,即dyld: lazy symbol binding failed后跟dyld: 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时删除了weird_module.so选项,则dyld错误会消失。

如果您从weird_module_function构造函数调用libsomething.dylib,但在调用main之前发生这种错误也会发生同样的错误,这可能不是您发生的事情。

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

要检查的其他一些事项:

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