创建带有只读标志的程序头会导致段错误
Creating a program header with read only flag causes segfault
我一直在使用 NASM 编写 ELF 二进制文件,并且我创建了一个打开了只读标志的段。 运行 程序导致段错误。我在 replit 中测试了这个程序,它 运行 很好所以问题是什么?我用 .rodata 部分中的 hello world 字符串创建了一个常规的 NASM hello world 程序,运行 很好。我用 readelf 检查了二进制文件以确保字符串在只读段中。
我想出的唯一解决方案是在 rodata 段中设置可执行标志,以便它具有读取/执行权限,但这是 hacky,我希望 rodata 段是只读的。
这是 ELF-64 hello world 的代码。
; hello.asm
[bits 64]
[org 0x400000]
fileHeader:
db 0x7F, "ELF"
db 2 ; ELF-64
db 1 ; little endian
db 1 ; ELF version
db 0 ; System V ABI
db 0 ; ABI version
db 0, 0, 0, 0, 0, 0, 0 ; unused
dw 2 ; executable object file
dw 0x3E ; x86-64
dd 1 ; ELF version
dq text ; entry point
dq 64 ; program header table offset
dq nullSection - $$ ; section header table offset
dd 0 ; flags
dw 64 ; size of file header
dw 56 ; size of program header
dw 3 ; program header count
dw 64 ; size of section header
dw 4 ; section header count
dw 3 ; section header string table index
nullSegment:
times 56 db 0
textSegment:
dd 1 ; loadable segment
dd 0x4 ; read / execute permissions
dq text - $$ ; segment offset
dq text ; virtual address of segment
dq 0 ; physical address of segment
dq textSize ; size of segment in file
dq textSize ; size of segment in memory
dq 0x1000 ; alignment
rodataSegment:
dd 1 ; loadable segment
dd 0x4 ; read permission (setting this flag to 0x5 causes the program to run just fine)
dq rodata - $$ ; segment offset
dq rodata ; virtual address of segment
dq 0 ; physical address of segment
dq rodataSize ; size of segment in file
dq rodataSize ; size of segment in memory
dq 0x1000 ; alignment
text:
mov rax, 1
mov rdi, 1
mov rsi, message
mov rdx, messageLength
syscall
mov rax, 60
xor rdi, rdi
syscall
textSize equ $ - text
rodata:
message db "Hello world!", 0xA, 0
messageLength equ $ - message
rodataSize equ $ - rodata
stringTable:
db 0
db ".text", 0
db ".rodata", 0
db ".shstrtab", 0
stringTableSize equ $ - stringTable
nullSection:
times 64 db 0
textSection:
dd 1 ; index into string table
dd 1 ; program data
dq 0x6 ; occupies memory & executable
dq text ; virtual address of section
dq text - $$ ; offset of section in file
dq textSize ; size of section in file
dq 0 ; unused
dq 0x1000 ; alignment
dq 0 ; unused
rodataSection:
dd 7 ; index into string table
dd 1 ; program data
dq 0x2 ; occupies memory
dq rodata ; virtual address of section
dq rodata - $$ ; offset of section in file
dq rodataSize ; size of section in file
dq 0 ; unused
dq 0x1000 ; no alignment
dq 0 ; unused
stringTableSection:
dd 15 ; index into string table
dd 3 ; string table
dq 0 ; no attributes
dq stringTable ; virtual address of section
dq stringTable - $$ ; offset of section in file
dq stringTableSize ; size of section in file
dq 0 ; unused
dq 0 ; no alignment
dq 0 ; unused
replitHello.asm: https://hastebin.com/ujanoguveq.properties // 它应该与第
行几乎相同
这是最小的 nasm hello world 程序。
; helloNasm.asm
section .text
global _start
_start:
mov rax, 1
mov rdi, 1
mov rsi, message
mov rdx, messageLength
syscall
mov rax, 60
xor rdi, rdi
syscall
section .rodata
message db "Hello NASM!", 0xA, 0
messageLength equ $ - message
textSegment:
dd 1 ; loadable segment
dd 0x4 ; read / execute permissions
我假设你的意思是 0x5
上面的标志。
修复后,我看到以下片段:
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
NULL 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 0
LOAD 0x0000e8 0x00000000004000e8 0x0000000000000000 0x000025 0x000025 R E 0x1000
LOAD 0x00010d 0x000000000040010d 0x0000000000000000 0x00000e 0x00000e R 0x1000
这要求内核在同一地址(0x400000
)执行两个mmap
。这些 mmap
中的第二个映射到第一个,导致以下 /proc/$pid/maps
:
00400000-00401000 r--p 00000000 fe:02 22548440 /tmp/t
7ffff7ff9000-7ffff7ffd000 r--p 00000000 00:00 0 [vvar]
7ffff7ffd000-7ffff7fff000 r-xp 00000000 00:00 0 [vdso]
7ffffffdd000-7ffffffff000 rw-p 00000000 00:00 0 [stack]
如您所见,程序文本 不可执行 ,因此程序 SIGSEGV
在第一条指令上执行:
(gdb) run
Starting program: /tmp/t
Program received signal SIGSEGV, Segmentation fault.
0x00000000004000e8 in ?? ()
(gdb) x/i $pc
=> 0x4000e8: mov [=13=]x1,%eax
要解决此问题,您必须将其中一个片段移动到不同的页面(正如 Jester 正确指出的那样)。
另请注意,部分 是完全没有必要的(只有部分很重要)。特别是在 .text
部分中设置 A X
标志对任何事情都没有影响。
我一直在使用 NASM 编写 ELF 二进制文件,并且我创建了一个打开了只读标志的段。 运行 程序导致段错误。我在 replit 中测试了这个程序,它 运行 很好所以问题是什么?我用 .rodata 部分中的 hello world 字符串创建了一个常规的 NASM hello world 程序,运行 很好。我用 readelf 检查了二进制文件以确保字符串在只读段中。
我想出的唯一解决方案是在 rodata 段中设置可执行标志,以便它具有读取/执行权限,但这是 hacky,我希望 rodata 段是只读的。
这是 ELF-64 hello world 的代码。
; hello.asm
[bits 64]
[org 0x400000]
fileHeader:
db 0x7F, "ELF"
db 2 ; ELF-64
db 1 ; little endian
db 1 ; ELF version
db 0 ; System V ABI
db 0 ; ABI version
db 0, 0, 0, 0, 0, 0, 0 ; unused
dw 2 ; executable object file
dw 0x3E ; x86-64
dd 1 ; ELF version
dq text ; entry point
dq 64 ; program header table offset
dq nullSection - $$ ; section header table offset
dd 0 ; flags
dw 64 ; size of file header
dw 56 ; size of program header
dw 3 ; program header count
dw 64 ; size of section header
dw 4 ; section header count
dw 3 ; section header string table index
nullSegment:
times 56 db 0
textSegment:
dd 1 ; loadable segment
dd 0x4 ; read / execute permissions
dq text - $$ ; segment offset
dq text ; virtual address of segment
dq 0 ; physical address of segment
dq textSize ; size of segment in file
dq textSize ; size of segment in memory
dq 0x1000 ; alignment
rodataSegment:
dd 1 ; loadable segment
dd 0x4 ; read permission (setting this flag to 0x5 causes the program to run just fine)
dq rodata - $$ ; segment offset
dq rodata ; virtual address of segment
dq 0 ; physical address of segment
dq rodataSize ; size of segment in file
dq rodataSize ; size of segment in memory
dq 0x1000 ; alignment
text:
mov rax, 1
mov rdi, 1
mov rsi, message
mov rdx, messageLength
syscall
mov rax, 60
xor rdi, rdi
syscall
textSize equ $ - text
rodata:
message db "Hello world!", 0xA, 0
messageLength equ $ - message
rodataSize equ $ - rodata
stringTable:
db 0
db ".text", 0
db ".rodata", 0
db ".shstrtab", 0
stringTableSize equ $ - stringTable
nullSection:
times 64 db 0
textSection:
dd 1 ; index into string table
dd 1 ; program data
dq 0x6 ; occupies memory & executable
dq text ; virtual address of section
dq text - $$ ; offset of section in file
dq textSize ; size of section in file
dq 0 ; unused
dq 0x1000 ; alignment
dq 0 ; unused
rodataSection:
dd 7 ; index into string table
dd 1 ; program data
dq 0x2 ; occupies memory
dq rodata ; virtual address of section
dq rodata - $$ ; offset of section in file
dq rodataSize ; size of section in file
dq 0 ; unused
dq 0x1000 ; no alignment
dq 0 ; unused
stringTableSection:
dd 15 ; index into string table
dd 3 ; string table
dq 0 ; no attributes
dq stringTable ; virtual address of section
dq stringTable - $$ ; offset of section in file
dq stringTableSize ; size of section in file
dq 0 ; unused
dq 0 ; no alignment
dq 0 ; unused
replitHello.asm: https://hastebin.com/ujanoguveq.properties // 它应该与第
行几乎相同这是最小的 nasm hello world 程序。
; helloNasm.asm
section .text
global _start
_start:
mov rax, 1
mov rdi, 1
mov rsi, message
mov rdx, messageLength
syscall
mov rax, 60
xor rdi, rdi
syscall
section .rodata
message db "Hello NASM!", 0xA, 0
messageLength equ $ - message
textSegment:
dd 1 ; loadable segment
dd 0x4 ; read / execute permissions
我假设你的意思是 0x5
上面的标志。
修复后,我看到以下片段:
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
NULL 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 0
LOAD 0x0000e8 0x00000000004000e8 0x0000000000000000 0x000025 0x000025 R E 0x1000
LOAD 0x00010d 0x000000000040010d 0x0000000000000000 0x00000e 0x00000e R 0x1000
这要求内核在同一地址(0x400000
)执行两个mmap
。这些 mmap
中的第二个映射到第一个,导致以下 /proc/$pid/maps
:
00400000-00401000 r--p 00000000 fe:02 22548440 /tmp/t
7ffff7ff9000-7ffff7ffd000 r--p 00000000 00:00 0 [vvar]
7ffff7ffd000-7ffff7fff000 r-xp 00000000 00:00 0 [vdso]
7ffffffdd000-7ffffffff000 rw-p 00000000 00:00 0 [stack]
如您所见,程序文本 不可执行 ,因此程序 SIGSEGV
在第一条指令上执行:
(gdb) run
Starting program: /tmp/t
Program received signal SIGSEGV, Segmentation fault.
0x00000000004000e8 in ?? ()
(gdb) x/i $pc
=> 0x4000e8: mov [=13=]x1,%eax
要解决此问题,您必须将其中一个片段移动到不同的页面(正如 Jester 正确指出的那样)。
另请注意,部分 是完全没有必要的(只有部分很重要)。特别是在 .text
部分中设置 A X
标志对任何事情都没有影响。