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

如何在Linux下用GCC生成flat格式hello world二进制并解决链接报错

报错根因

你遇到的PHDR segment not covered by LOAD segment错误确实和-N(对应ld的--omagic选项)直接相关:

  • -N选项的作用是合并.text、.data段,取消.text段的只读保护,同时关闭段的页面对齐
  • GNU ld 2.36及以上版本新增了段合规性检查,默认链接脚本下-N生成的ELF的程序头表(PHDR段)偏移不在合并后的唯一LOAD段范围内,因此触发报错

旧版本ld不会抛出该错误,但生成的ELF本身不符合程序头表必须被LOAD段覆盖的规范。

第一步:修正代码潜在问题

你的现有代码存在几个功能性问题,需要先调整:

  • 拆分的两个内联汇编块中间可能被编译器插入修改rax的指令,导致write系统调用的返回值获取错误
  • write的目标文件描述符错写为0(标准输入),标准输出的文件描述符是1
  • sizeof(msg)取的是指针的大小(64位环境下为8),而非字符串实际长度
  • 缺少退出系统调用,程序执行完write后会跑飞触发段错误

修正后的代码如下:

#include <sys/types.h>

static inline ssize_t _write(int fd, const void *buf, size_t count) {
  ssize_t ret;
  __asm__ volatile(
    "mov %1, %%rdi\n"
    "mov %2, %%rsi\n"
    "mov %3, %%rdx\n"
    "mov $1, %%rax\n"
    "syscall\n"
    "mov %%rax, %0\n"
    : "=r"(ret)
    : "g"(fd), "g"(buf), "g"(count)
    : "rdi", "rsi", "rdx", "rax", "memory");
  return ret;
}

int _start() {
  const char msg[] = "hello world\n";
  _write(1, msg, sizeof(msg) - 1);
  /* 调用exit系统调用正常退出 */
  __asm__ volatile(
    "mov $60, %%rax\n"
    "mov $0, %%rdi\n"
    "syscall\n"
    ::: "rax", "rdi");
  __builtin_unreachable();
}
第二步:编译生成flat binary

不要直接用-N生成ELF,更稳妥的方式是先编译链接成合规ELF,再通过objcopy提取纯二进制内容,步骤如下:

  1. 编译生成重定位目标文件:
gcc -std=c99 -fPIC -fno-stack-protector -ffreestanding -c hello.c -o hello.o
  1. 链接生成ELF可执行文件,指定入口为_start,不依赖标准库:
ld -nostdlib -Ttext 0x1000 hello.o -o hello.elf

其中0x1000为程序的加载基地址,如果你需要完全位置无关的程序,可以将参数替换为-pie。
3. 提取所需段合并为flat binary:

objcopy -O binary --only-section=.text --only-section=.data --only-section=.rodata hello.elf hello.bin

最终生成的hello.bin就是你需要的单段自包含flat binary,程序入口对应文件开头,所有代码、数据、只读数据都合并在同一个连续地址空间内。

如果你需要直接生成单段ELF而非纯二进制,可以用-z noseparate-code参数替代-N,或者编写自定义链接脚本将PHDR段包含到LOAD段范围内即可解决报错。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 00:00:05