Ken*_*her 1 linux glibc dynamic-linking linux-kernel
我有一个非常简单的ELF可执行文件:
$ readelf -l ./plt.out
Elf file type is EXEC (Executable file)
Entry point 0x400338
There are 7 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x00000000003ff040 0x00000000003ff040
0x0000000000000188 0x0000000000000188 R E 8
LOAD 0x0000000000000000 0x00000000003ff000 0x00000000003ff000
0x0000000000001000 0x0000000000001000 RW 1000
INTERP 0x00000000000001c8 0x00000000003ff1c8 0x00000000003ff1c8
0x0000000000000032 0x0000000000000032 R 1
[Requesting program interpreter: /data/keno/new_glibc/usr/lib/ld-linux-x86-64.so.2]
LOAD 0x0000000000001000 0x0000000000400000 0x0000000000400000
0x00000000000003b0 0x00000000000003b0 R E 1000
LOAD 0x0000000000001ea0 0x0000000000600ea0 0x0000000000600ea0
0x0000000000000180 0x0000000000000180 RW 1000
DYNAMIC 0x0000000000001ea0 0x0000000000600ea0 0x0000000000600ea0
0x0000000000000150 0x0000000000000150 RW 8
GNU_RELRO 0x0000000000001ea0 0x0000000000600ea0 0x0000000000600ea0
0x0000000000000160 0x0000000000000160 R 1
Run Code Online (Sandbox Code Playgroud)
现在,根据我对ELF如何工作的理解,我希望有三个部分:
0x3ff000-0x4000000x400000-0x4010000x600000-0x602000(0xea0+0x180 > 0x1000)的一个RW但是,当我实际查看在运行可执行文件时获得的内容时/proc/pid/maps,我会看到以下内容:
003ff000-00400000 rwxp 00000000 00:28 1456774 plt.out
00400000-00401000 r-xp 00001000 00:28 1456774 plt.out
00600000-00601000 r-xp 00001000 00:28 1456774 plt.out
00601000-00602000 rwxp 00002000 00:28 1456774 plt.out
Run Code Online (Sandbox Code Playgroud)
这根本不是我的预期.这里发生了什么?
这里的答案是双重的,一部分由动态链接器提供,另一部分由内核提供.为了看到这一点,让我们在进入动态链接器后立即查看内存映射(例如,通过在_dl_start中设置断点).我们看:
003ff000-00400000 rwxp 00000000 00:28 1456774 plt.out
00400000-00401000 r-xp 00001000 00:28 1456774 plt.out
00600000-00602000 rwxp 00001000 00:28 1456774 plt.out
Run Code Online (Sandbox Code Playgroud)
这至少更接近我们想要的东西(它在正确的位置有正确的段).现在,最后一个片段被拆分的原因是因为GNU_RELRO程序头,它对动态链接器说"嘿,在你完成初始重定位后我不再需要写这个了",所以动态链接器忠实地尝试将该内存区域设置为PROT_READ(注意它忽略了程序头中设置的实际权限标志,尽管它们看起来通常设置为PF_R).
这只是神秘的一半.我们还有那些令人讨厌的PROT_EXEC位,我们没有订购.结果归结为linux内核的一个名为READ_IMPLIES_EXEC个性的功能,导致所有具有PROT_READ权限的地图也具有PROT_EXEC权限(参见个性化手册页(2)).事实证明,出于兼容性原因,除非PT_GNU_STACK程序头告诉它不要,否则linux会自动设置此个性.如果所有输入对象都有(空).note.GNU-stack部分,则链接器会自动创建此程序头.有关该机制的更多信息,请参见此处.