问题描述
之前在 x86_64 Linux 上多次看到的基本 Hello World:
global my_start_symbol
section .text
my_start_symbol:
mov rax,1
mov rdi,1
mov rsi,msg
mov rdx,msg_len
syscall
mov rax,60
xor rdi,rdi
syscall
section .rodata:
msg: db "Hello,world!",10
msg_len: equ $ - msg
我的工作 ld
链接器脚本:
ENTRY(my_start_symbol)
SECTIONS
{
. = 0x10000;
.text : { *(.text*) }
.rodata : { *(.rodata*) }
}
调用方式:
nasm -f elf64 assembly.asm -o assembly.o
ld -T linker.ld assembly.o -o assembly
我在尝试以下更改时遇到各种分段错误:
- 如果我删除链接描述文件中的
. = 0x10000
或使其小于0x10000
,我会收到段错误。我认为这可能是由于页面大小的原因,但是getconf PAGE_SIZE
返回 4K,所以我不知道为什么需要 8K。 - 如果我将程序集文件中的
.text
部分更改为.my_section_name
并将链接器更新为.my_section_name : { *(.my_section_name*) }
,则会出现段错误。我认为像.text
、.data
等部分名称是约定俗成的,您可以根据需要将它们设为任何名称。如果我错了,为什么.text : { *(.my_section_name*) }
也会给出段错误?
解决方法
0x10000
最小值可能是由于 vm.mmap_min_addr = 65536
(https://wiki.debian.org/mmap_min_addr) 的默认设置,阻止用户空间映射任何低地址页面以确保 NULL-deref 错误嘈杂,即使对于像 ptr[i]
这样具有一些中等索引的代码。
一些像 .text
这样的名称是特殊的(对于 NASM 和/或 ld),并且具有一组默认的权限(在这种情况下为 read + exec)。一些随机名称可能是没有 exec 的读 + 写。检查 readelf -a a.out
,尤其是程序头(管理内核如何将可执行文件映射到内存中)。
查看 readelf -a hello.o
以检查 NASM 输出中的部分标题也可能很有趣。