我有一个非常奇怪的情况与动态符号绑定在OSX上,我希望得到一些线索如何解决。
我有一个用c编写的应用程序,它使用dlopen()
在运行时动态加载模块。其中一些模块导出全局符号,以后加载的其他模块可能会使用这些符号。
我们有一个模块(我称之为weird_module.so
)导出全局符号,其中一个是weird_module_function
。如果怪异的模块so链接到特定的库(我将称之为libsomething.dylib
),则无法绑定到。但如果在链接时删除weird_module_function
,则可以绑定到-lsomething
。weird_module.so
可能会发生什么情况,导致weird_module_function
无法导出符号?我可以做些什么来调试如何导出符号(类似于如何使用libsomething.dylib
调试如何绑定符号)?
$ 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!
编辑:
我试着组装一个最小的应用程序来复制这个问题,在这样做的过程中,至少发现了一件我们做错了的事情。还有另外两个与重复这个问题有关的事实。
首先,
weird_module.so
使用DYLD_PRINT_BINDINGS
预加载模块以检查其元数据。然后根据元数据,使用run-app
或RTLD_LAZY | RTLD_LOCAL
打开并重新打开模块。(对于所讨论的两个模块,它将使用dlclose()
重新打开)。其次,对于a
RTLD_LAZY | RTLD_GLOBAL
全局,在RTLD_NOW | RTLD_LOCAL
和RTLD_LAZY | RTLD_GLOBAL
中存在符号冲突。$ 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
无关。我触发这个错误的方法是从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
以了解调用的构造函数。还有一些需要检查的地方:
您是否100%确定两个模块都是用
RTLD_LAZY | RTLD_GLOBAL
重新打开的?唯一能得到dyld错误的方法是传递RTLD_LOCAL
选项。是否确定
dlclose
调用成功(返回0)?例如,如果您的模块包含objective-c代码,则不会卸载它。关于c - 在OS X上动态符号绑定(bind)的详细信息是什么?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/18701872/