MvG*_*MvG 17 linux dynamic-linking dynamic-loading weak-linking symbol-table
分析这个问题,我dlopen在Linux上的动态加载()上下文中发现了一些关于弱符号解析行为的事情.现在我正在寻找管理这个的规范.
我们来举个例子吧.假设有一个程序a可以动态加载库b.so,并c.so按此顺序.如果c.so依赖于另外两个库foo.so(实际上libgcc.so在该示例中)和bar.so(实际上libpthread.so),则通常bar.so可以使用导出的符号来满足弱符号链接foo.so.但是,如果b.so还依赖foo.so但不依赖bar.so,那么这些弱的符号显然不会被联系起来bar.so.看起来好像foo.so墨水只是从a和b.so它们的所有依赖项中寻找符号.
这在某种程度上是有道理的,因为否则加载c.so可能会改变已经使用库的foo.so某些点的行为b.so.另一方面,在让我开始的问题中,这引起了相当多的麻烦,所以我想知道是否有办法解决这个问题.为了找到解决方法,我首先需要很好地理解在这些情况下如何指定符号解析的非常精确的细节.
在这些场景中定义正确行为的规范或其他技术文档是什么?
Pra*_*tic 12
不幸的是,权威文档是源代码.大多数Linux发行版都使用glibc或它的fork,例如eglibc.在两者的源代码中,应该记录dlopen()的文件如下所示:
手动/ libdl.texi
@c FIXME these are undocumented:
@c dladdr
@c dladdr1
@c dlclose
@c dlerror
@c dlinfo
@c dlmopen
@c dlopen
@c dlsym
@c dlvsym
Run Code Online (Sandbox Code Playgroud)
可以从ELF规范和POSIX标准中得出什么技术规范.ELF规范使弱符号变得有意义.POSIX是dlopen()本身的实际规范.
这是我发现ELF规范中最相关的部分.
当链接编辑器搜索归档库时,它会提取包含未定义全局符号定义的归档成员.成员的定义可以是全局符号或弱符号.
ELF规范没有提到动态加载,因此本段的其余部分是我自己的解释.我发现上述相关的原因是解析符号出现在单个"何时".在您给出的示例中,当程序a动态加载时b.so,动态加载程序尝试解析未定义的符号.最终可能会使用全局或弱符号.当程序随后动态加载时c.so,动态加载程序再次尝试解析未定义的符号.在您描述的场景中,符号b.so用弱符号解析.一旦解决,这些符号就不再是未定义的.使用全局符号或弱符号来定义它们并不重要.c.so加载的时间已经不再是未定义的.
ELF规范没有精确定义链接编辑器的内容或链接编辑器必须组合目标文件的时间.据推测,这是一个非问题,因为该文档考虑了动态链接.
POSIX描述了一些dlopen()功能,但实际上还有很多,包括问题的实质内容.POSIX一般不提及ELF格式或弱符号.对于实现dlopen()的系统,甚至不需要任何弱符号的概念.
http://pubs.opengroup.org/onlinepubs/9699919799/functions/dlopen.html
POSIX合规性是另一个标准Linux标准库的一部分.Linux发行版可能会也可能不会选择遵循这些标准,可能会或可能不会成为认证的麻烦.例如,据我所知,Open Group正式的Unix认证非常昂贵 - 因此大量的"类Unix"系统.
关于dlopen()的标准符合性的一个有趣的观点是在维基百科的动态加载文章中提出的.按照POSIX的要求,dlopen()返回一个void*,但是,按照ISO的要求,C表示void*是一个指向对象的指针,这样的指针不一定与函数指针兼容.
事实上,函数和对象指针之间的任何转换都必须被视为(固有的非可移植的)实现扩展,并且不存在直接转换的"正确"方式,因为在这方面,POSIX和ISO标准相互矛盾.其他.
确实存在的标准是矛盾的,无论如何标准文件可能都没有特别的意义.这是Ulrich Drepper写的关于他对Open Group及其"规范"的蔑视.
http://udrepper.livejournal.com/8511.html
在罗德里戈联系的帖子中表达了类似的情绪.
我做出这种改变的原因并不是更加符合要求(这很好,但没有理由,因为没有人抱怨旧的行为).
在调查之后,我相信问题的正确答案正如你所问的那样,dlopen()在这方面没有正确或错误的行为.可以说,一旦搜索解析了符号,它就不再是未定义的,并且在随后的搜索中,动态加载器将不会尝试解析已定义的符号.
最后,正如您在评论中所述,您在原始帖子中描述的内容不正确.动态加载的共享库可用于解析先前动态加载的共享库中的未定义符号.实际上,这不仅限于动态加载代码中的未定义符号.下面是一个示例,其中可执行文件本身具有通过动态加载解析的未定义符号.
main.c中
#include <dlfcn.h>
void say_hi(void);
int main(void) {
void* symbols_b = dlopen("./dyload.so", RTLD_NOW | RTLD_GLOBAL);
/* uh-oh, forgot to define this function */
/* better remember to define it in dyload.so */
say_hi();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
dyload.c
#include <stdio.h>
void say_hi(void) {
puts("dyload.so: hi");
}
Run Code Online (Sandbox Code Playgroud)
编译并运行.
gcc-4.8 main -fpic -ldl -Wl,--unresolved-symbols=ignore-all -o main
gcc-4.8 dyload.c -shared -fpic -o dyload.so
$ ./main
dyload.so: hi
Run Code Online (Sandbox Code Playgroud)
请注意,主可执行文件本身已编译为PIC.
| 归档时间: |
|
| 查看次数: |
6112 次 |
| 最近记录: |