You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用GNU ld与仅含.text段的链接脚本制作的最小静态ELF可执行文件为何无法运行?

问题:Linux加载器拒绝执行精简程序头的ELF可执行文件

本文末尾的Base64编码ELF可执行文件是一个尝试将程序头精简到极致的实验产物。作者认为它完全合法,但Linux可执行加载器却拒绝执行:

$ strace ./experiment
execve("./experiment", ["./experiment"], 0x7ffee000bd20 /* 69 vars */) = -1 EPERM (Operation not permitted)

以下是该文件的readelf输出及程序头信息:

$ readelf -hl ./experiment
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's补码,小端序
  Version:                           1(当前版本)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC(可执行文件)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x120
  Start of program headers:          64(文件内偏移字节数)
  Start of section headers:          512(文件内偏移字节数)
  Flags:                             0x0
  Size of this header:               64(字节)
  Size of program headers:           56(字节)
  Number of program headers:         4
  Size of section headers:           64(字节)
  Number of section headers:         6
  Section header string table index: 5

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000190 0x0000000000000190  R E    0x1000
  NOTE           0x0000000000000160 0x0000000000000160 0x0000000000000160
                 0x0000000000000030 0x0000000000000030  R      0x8
  GNU_PROPERTY   0x0000000000000160 0x0000000000000160 0x0000000000000030  R      0x8
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10

 Section to Segment mapping:
  Segment Sections...
   00     .text .note.gnu.property 
   01     .note.gnu.property 
   02     .note.gnu.property 
   03     

Linux程序加载器为何不接受此文件?


Base64编码的程序

f0VMRgIBAQAAAAAAAAAAAAIAPgABAAAAIAEAAAAAAABAAAAAAAAAAAACAAAAAAAAAAAAAEAAOAAE
AEAABgAFAAEAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkAEAAAAAAACQAQAAAAAAAAAQ
AAAAAAAABAAAAAQAAABgAQAAAAAAAGABAAAAAAAAYAEAAAAAAAAwAAAAAAAAADAAAAAAAAAACAAA
AAAAAABT5XRkBAAAAGABAAAAAAAAYAEAAAAAAABgAQAAAAAAADAAAAAAAAAAMAAAAAAAAAAIAAAA
AAAAAFHldGQGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA
AAAASInnSIHnAPD//0jHxgAQAABIx8ALAAAADwVIhcB1C0jHwCIAAAAPBev1SInHSPffSMfAPAAA
AA8FAAAAAAAAAAQAAAAgAAAABQAAAEdOVQABAAHABAAAAAEAAAAAAAAAAgABwAQAAAABAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAASAAEAIAEAAAAAAAA5AAAAAAAAAABfc3RhcnQA
AC5zeW10YWIALnN0cnRhYgAuc2hzdHJ0YWIALnRleHQALm5vdGUuZ251LnByb3BlcnR5AAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAGwAAAAEAAAAGAAAAAAAAACABAAAAAAAAIAEAAAAAAAA5AAAAAAAAAAAAAAAAAAAAAQAA
AAAAAAAAAAAAAAAAACEAAAAHAAAAAgAAAAAAAABgAQAAAAAAAGABAAAAAAAAMAAAAAAAAAAAAAAA
AAAAAAgAAAAAAAAAAAAAAAAAAAABAAAAAgAAAAAAAAAAAAAAAAAAAAAAAACQAQAAAAAAADAAAAAA
AAAABAAAAAEAAAAIAAAAAAAAABgAAAAAAAAACQAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAwAEAAAAA
AAAIAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAABEAAAADAAAAAAAAAAAAAAAAAAAAAAAA
AMgBAAAAAAAANAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAA=

可复现该程序的步骤

cat > experiment.S <<\EOF
#include <sys/syscall.h>
        .text
        .global _start
        .type _start, @function
_start:
        mov %rsp, %rdi
        and $(~4095), %rdi
        mov $4096, %rsi
        mov $SYS_munmap, %rax
        syscall
        test %rax, %rax
        jne .Lfail

.Lloop:
        mov $SYS_pause, %rax
        syscall
        jmp .Lloop

.Lfail:
        mov %rax, %rdi
        neg %rdi
        mov $SYS_exit, %rax
        syscall

        .size _start, .-_start
        .section .note.GNU-stack,"",@progbits
EOF

cat > experiment.ld <<\EOF
OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")
OUTPUT_ARCH(i386:x86-64)
ENTRY(_start)
SECTIONS
{
  . = SEGMENT_START("text-segment", 0) + SIZEOF_HEADERS;
  .text : { *(.text) }
}
EOF

cc -P -E experiment.S | as -o experiment.o -
ld -o experiment -static -e _start -T experiment.ld experiment.o

解答

问题出在LOAD段的虚拟地址设置上:该ELF的LOAD段指定VirtAddr为0x0,而x86_64 Linux内核默认会禁止映射虚拟地址0开始的内存区域——这是一种安全机制,用于捕获NULL指针解引用的错误。当加载器尝试将LOAD段映射到地址0时,内核会拒绝这个操作,最终导致execve系统调用返回EPERM错误。

要修复这个问题,只需要修改链接脚本experiment.ld,将SEGMENT_START("text-segment", 0)中的0改为一个合法的非低地址值,比如0x400000(x86_64 ELF可执行文件的默认起始地址):

. = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS;

重新编译链接后,程序就能正常执行了。


内容的提问来源于stack exchange,提问作者zwol

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.01 13:19:51