据我了解,当我编译 C 源代码时,GCC/LD 在编译时进行地址绑定。通常,该地址从零开始。当我从共享库调用函数时,编译器如何提前知道共享库的地址?我知道共享库在加载时获取它们的地址。如果我错了,请纠正我。
答案1
您可以做一些事情来帮助自己理解这一点。
ldd /usr/bin/cat
在我的笔记本电脑上,输出如下:
1771 % ldd /usr/bin/cat
linux-vdso.so.1 (0x00007ffc37fba000)
libc.so.6 => /usr/lib/libc.so.6 (0x00007f1ea7018000)
/lib64/ld-linux-x86-64.so.2 (0x00007f1ea73bc000)
您会注意到,linux-vdso.so.1
并且libc.so.6
没有完全限定的路径,这会ldd
向您显示用于动态链接的实际路径libc.so.6
是什么。
您可以通过再次进行检查(并学习一些)ldd
,但有一点变化:
1790 % export LD_LIBRARY_PATH=/lib
1791 % ldd /usr/bin/cat
linux-vdso.so.1 (0x00007fff0a5a0000)
libc.so.6 => /lib/libc.so.6 (0x00007fa257535000)
/lib64/ld-linux-x86-64.so.2 (0x00007fa2578d9000)
仔细观察,您可以看到动态链接器(/lib64/ld-linux-x86-64.so.2
在本例中)现在将选择/lib/libc.so.6
C 库。因此环境变量 LD_LIBRARY_PATH 对于决定动态链接中实际使用哪些文件非常重要。
您可以做的另一件事是strace
:
strace -o cat.trace /usr/bin/cat /etc/motd
该文件cat.trace
将记录您的主题进程执行的系统调用。其中一些将是显示为动态链接的mmap()
文件。通过谷歌搜索.ldd
mmap
进一步阅读:man ld.so
.
查看是否readelf
安装了该命令,或者是否可以安装它,或者编译它。跑步readelf -a /usr/bin/cat
。实际上ELF规范在我看来是难以理解的,但是有一些 好的ELF格式信息在那里。尝试找到它。
如果您有musl libc
安装,或者可以安装它,在使用 musl libc 编译的简单可执行文件上进行与上面相同的实验可以提供非常丰富的信息。即使像动态链接这样复杂的东西也可以在同一操作系统上有两种不同的、有效的实现。