仅仅是好奇心,Linux动态链接器/加载器ld-linux.so*
本身是如何链接和加载的?
上面的屏幕截图显示file
和ldd
似乎给出了相互矛盾的结果:一个说静态链接,另一个说动态链接
然后如何装载装载器?
答案 0 :(得分:2)
ld-linux.so*
不依赖于任何其他库。加载到内存时它可以自行运行。
ldd
是一个脚本,它通过加载器加载目标文件,加载器检查对象是动态链接还是静态链接,试试这个:
LD_TRACE_LOADED_OBJECTS = 1 /lib64/ld-linux-x86-64.so.2 /lib64/ld-linux-x86-64.so.2
file
读取幻数或精灵标头以确定对象是动态链接还是静态链接,它可能会输出ldd
IMO,ld-linux.so
是静态链接的,因为它没有.interp
部分,所有动态链接的对象都必须具有该部分。
答案 1 :(得分:1)
@Zang MingJie
你的回答给了我很多帮助,但下面的话可能会让一些人感到困惑:
IMO,ld-linux.so是静态链接的,因为它没有.interp>部分,所有动态链接的对象都必须具有。
我们应该将“所有动态链接对象”分为两部分,我们称之为“共享对象”的一种是这样生成的:
gcc -c -o test.o test.c -fPIC
ld -o test.so test.o -shared
另一种称为“动态链接可执行文件”:
gcc -c -o test.o test.c -fPIC
ld -o test.so test.o
两点很重要:
1,共享对象没有'.iNTERP'段,而动态链接可执行文件有。
2,Linux内核不关心ELF文件是由EXF标头指示的EXEC还是DYN。他首先搜索.INTERP段,如果失败,他mmap()每个LOAD类型段,并将控件传递给eheader-> e_entry,无论他是否正在加载可执行文件或共享对象。
由于ld-linux.so是一个常见的共享对象,因此她不拥有.INTERP段并不奇怪。并且她可以作为可执行文件运行并不奇怪。每个共享对象都可以。
编写如下代码:
void foobar(void){ while(1); }
将其编译为共享对象(使用上面的命令行)。 跑吧:
gdb ./test.so
你的进程会陷入死循环。 使用Ctrl-C中断它。你会看到(需要gcc的-g选项)
Program received signal SIGINT, Interrupt.
foobar (void) at test.c:1
1 while(1);
(gdb)
你可以走得更远:
(gdb) p $eip
$1 = (void (*)()) 0x80000183 <foobar+3>
(gdb)
如果你熟悉linux内核,你应该知道0x80000000与内核变量'mmap_min_addr'的值有关。因为test.so是一个共享对象,她的加载地址为零,所以内核找到了她的默认虚拟地址,即0x80000000,而不是0x804000。
我不知道我是如何偏离主题的......