ld-linux.so* 本身是如何链接和加载的?
how is ld-linux.so* itself linked and loaded?
出于好奇,Linux 动态 linker/loader ld-linux.so*
本身是如何链接和加载的?
上面的截图显示file
和ldd
似乎给出了矛盾的结果:一个说静态链接,另一个说动态链接.
那么loader本身是如何加载的呢?
ld-linux.so*
不依赖任何其他库。加载到内存后可自行运行。
ldd
是一个脚本,它通过加载器加载目标文件,加载器检查对象是动态链接还是静态链接,试试这个:
LD_TRACE_LOADED_OBJECTS=1 /lib64/ld-linux-x86-64.so.2 /lib64/ld-linux-x86-64.so.2
file
读取magic number或elf header来判断对象是动态链接还是静态链接,它可能输出与ldd
不同的值
IMO,ld-linux.so
是静态链接的,因为它没有所有动态链接对象必须具有的 .interp
部分。
@臧明杰
你的回答对我帮助很大,但以下的话可能会让一些人感到困惑:
IMO, ld-linux.so is static linked, because it doesn't have an .interp >section which all dynamically linked object must have.
我们应该把"all dynamic linked object"分成两部分,我们称之为'shared object'的一种是这样生成的:
gcc -c -o test.o test.c -fPIC
ld -o test.so test.o -shared
还有一种叫做'dynamic linked executable file':
gcc -c -o test.o test.c -fPIC
ld -o test.so test.o
有两点很重要:
1,Shared object 没有 '.iNTERP' 段,而 Dynamic 链接的可执行文件有。
2, Linux 内核不关心ELF 文件是由elf header 指示的EXEC 还是DYN。他首先搜索.INTERP 段,如果失败,他对每个LOAD 类型段进行mmap(),并将控制权传递给eheader->e_entry,无论他是加载可执行文件还是共享object.
因为 ld-linux.so 是一个共同的共享 object,所以她不拥有 .INTERP 段也就不足为奇了。而且她可以 运行 作为可执行文件并不奇怪。每个共享 object 都可以。
这样写代码:
void foobar(void){ while(1); }
将其编译成共享文件 object(使用上面的命令行)。
运行它:
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
= (void (*)()) 0x80000183 <foobar+3>
(gdb)
如果你熟悉linux内核,你应该知道0x80000000与内核变量'mmap_min_addr'的值有关。因为test.so是一个共享的object,她的load-address是零,所以内核为她找到了一个默认的虚拟地址,也就是0x80000000,而不是0x804000。
我不知道我怎么会这样off-topic ...
出于好奇,Linux 动态 linker/loader ld-linux.so*
本身是如何链接和加载的?
上面的截图显示
file
和ldd
似乎给出了矛盾的结果:一个说静态链接,另一个说动态链接.那么loader本身是如何加载的呢?
ld-linux.so*
不依赖任何其他库。加载到内存后可自行运行。ldd
是一个脚本,它通过加载器加载目标文件,加载器检查对象是动态链接还是静态链接,试试这个:
LD_TRACE_LOADED_OBJECTS=1 /lib64/ld-linux-x86-64.so.2 /lib64/ld-linux-x86-64.so.2
file
读取magic number或elf header来判断对象是动态链接还是静态链接,它可能输出与ldd
不同的值
IMO,ld-linux.so
是静态链接的,因为它没有所有动态链接对象必须具有的 .interp
部分。
@臧明杰
你的回答对我帮助很大,但以下的话可能会让一些人感到困惑:
IMO, ld-linux.so is static linked, because it doesn't have an .interp >section which all dynamically linked object must have.
我们应该把"all dynamic linked object"分成两部分,我们称之为'shared object'的一种是这样生成的:
gcc -c -o test.o test.c -fPIC
ld -o test.so test.o -shared
还有一种叫做'dynamic linked executable file':
gcc -c -o test.o test.c -fPIC
ld -o test.so test.o
有两点很重要:
1,Shared object 没有 '.iNTERP' 段,而 Dynamic 链接的可执行文件有。
2, Linux 内核不关心ELF 文件是由elf header 指示的EXEC 还是DYN。他首先搜索.INTERP 段,如果失败,他对每个LOAD 类型段进行mmap(),并将控制权传递给eheader->e_entry,无论他是加载可执行文件还是共享object.
因为 ld-linux.so 是一个共同的共享 object,所以她不拥有 .INTERP 段也就不足为奇了。而且她可以 运行 作为可执行文件并不奇怪。每个共享 object 都可以。
这样写代码:
void foobar(void){ while(1); }
将其编译成共享文件 object(使用上面的命令行)。 运行它:
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
= (void (*)()) 0x80000183 <foobar+3>
(gdb)
如果你熟悉linux内核,你应该知道0x80000000与内核变量'mmap_min_addr'的值有关。因为test.so是一个共享的object,她的load-address是零,所以内核为她找到了一个默认的虚拟地址,也就是0x80000000,而不是0x804000。
我不知道我怎么会这样off-topic ...