使用自定义链接器脚本了解简单“Hello World”的分段错误 - 低于 64k 的部分名称或基址

问题描述

之前在 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

我在尝试以下更改时遇到各种分段错误

  1. 如果我删除链接描述文件中的 . = 0x10000 或使其小于 0x10000,我会收到段错误。我认为这可能是由于页面大小的原因,但是 getconf PAGE_SIZE 返回 4K,所以我不知道为什么需要 8K。
  2. 如果我将程序集文件中的 .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 输出中的部分标题也可能很有趣。