【发布时间】:2016-05-04 11:14:01
【问题描述】:
出于好奇,Linux 动态链接器/加载器ld-linux.so* 本身是如何链接和加载的?
上面的截图显示
file和ldd似乎给出了矛盾的结果:一个说静态链接,另一个说动态链接。那么loader本身是如何加载的呢?
【问题讨论】:
标签: linux-kernel linker ld loader
出于好奇,Linux 动态链接器/加载器ld-linux.so* 本身是如何链接和加载的?
上面的截图显示file和ldd似乎给出了矛盾的结果:一个说静态链接,另一个说动态链接。
那么loader本身是如何加载的呢?
【问题讨论】:
标签: linux-kernel linker ld loader
ld-linux.so* 不依赖于任何其他库。加载到内存后可自行运行。
ldd是一个脚本,它通过loader加载object文件,loader检查object是动态链接还是静态链接,试试这个:
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 部分。
【讨论】:
@臧明杰
你的回答对我帮助很大,但下面的话可能会让一些人感到困惑:
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,共享对象没有'.iINTERP'段,而动态链接的可执行文件有。
2,Linux内核不关心ELF文件是由elf头指示的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。
我不知道我是怎么跑题的……
【讨论】: