但我不明白为什么Linux需要在运行时加载这些头文件。
它
不
.
它们是用来干什么的?如果进程运行需要它们,Linux不能自己加载吗?
要回答所有这些问题,您需要查看Linux内核源代码。
In
the source
,你可以看到,事实上程序头
不
需要成为任何
PT_LOAD
段,内核将自己读取它们。
按如下方式更改原始程序:
diff -u exe.asm.orig exe.asm
--- exe.asm.orig 2021-02-07 18:54:34.449336515 -0800
+++ exe.asm 2021-02-07 18:53:19.773532451 -0800
@@ -24,9 +24,9 @@
programHeader:
dd 1 ; p_type
dd 7 ; p_flags
- dq 0 ; p_offset
- dq $$ ; p_vaddr
- dq $$ ; p_paddr
+ dq _start - $$ ; p_offset
+ dq _start ; p_vaddr
+ dq _start ; p_paddr
dq fileSize ; p_filesz
dq fileSize ; p_memsz
dq 0x1000 ; p_align
生成一个运行良好的程序,但程序头不在
PT_LOAD
分段:
eu-readelf --all exe
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Ident Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: AMD x86-64
Version: 1 (current)
Entry point address: 0x8048078
Start of program headers: 64 (bytes into file)
Start of section headers: 0 (bytes into file)
Flags:
Size of this header: 64 (bytes)
Size of program header entries: 56 (bytes)
Number of program headers entries: 1
Size of section header entries: 0 (bytes)
Number of section headers entries: 0 ([0] not available)
Section header string table index: 0
Section Headers:
[Nr] Name Type Addr Off Size ES Flags Lk Inf Al
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000078 0x0000000008048078 0x0000000008048078 0x000081 0x000081 RWE 0x1000
我已尝试添加填充
你做得不对。使用“带填充”的源代码会产生以下结果
exe-padding
:
...
Entry point address: 0x8049000
...
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x001000 0x0000000008048000 0x0000000008048000 0x000009 0x000009 RWE 0x1000
此二进制文件由内核启动,并立即跳转到起始地址
0x8049000
,其中
未映射
(因为它不在
PT_LOAD
分段),导致立即
SIGSEGV
.
要解决此问题,您需要调整条目地址:
diff -u exe-padding.asm.orig exe-padding.asm
--- exe-padding.asm.orig 2021-02-07 18:57:31.800871195 -0800
+++ exe-padding.asm 2021-02-07 19:34:27.303071700 -0800
@@ -8,7 +8,7 @@
dw 2 ; e_type
dw 62 ; e_machine
dd 1 ; e_version
- dq _start ; e_entry
+ dq _start - 0x1000 ; e_entry
dq programHeader - $$ ; e_phoff
dq 0 ; e_shoff
dd 0 ; e_flags
这再次生成了一个可执行的工作文件。正式声明:
eu-readelf --all exe-padding
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Ident Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: AMD x86-64
Version: 1 (current)
Entry point address: 0x8048000
Start of program headers: 64 (bytes into file)
Start of section headers: 0 (bytes into file)
Flags:
Size of this header: 64 (bytes)
Size of program header entries: 56 (bytes)
Number of program headers entries: 1
Size of section header entries: 0 (bytes)
Number of section headers entries: 0 ([0] not available)
Section header string table index: 0
Section Headers:
[Nr] Name Type Addr Off Size ES Flags Lk Inf Al
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x001000 0x0000000008048000 0x0000000008048000 0x000009 0x000009 RWE 0x1000
附言:您正在将64位程序链接到
0x08048000
,这是的传统加载地址
i*86
(32位)可执行文件。
x86_64
二进制文件传统上从
0x400000
.
更新:
关于第一个例子,p_filesz仍然是fileSize,我认为这应该超出文件的边界。
这是正确的:
p_filesz
和
p_memsz
应减小集管的大小(
0x78
这里)。请注意,这两个值都将四舍五入到页面大小(添加后
p_offset
),所以对于这个例子,没有实际区别。
更新2:
pastebin.ubuntu.com/p/rgfVMrbcmJ
这导致了以下结果
LOAD
分段:
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000078 0x0000000008048000 0x0000000008048000 0x000081 0x000081 RWE 0x1000
这个二进制文件将不会运行(内核将拒绝它),因为它要求内核做不可能的事情:
mmap
偏移字节数
0x78
页面开始。
如果应用程序执行了等效操作
mmap
电话,它会得到
EINVAL
错误,因为
mmap
要求
(offset % pagesize) == (addr % pagesize)
.