生成最小ELF文件的C代码运行时出现段错误,请求排查
问题分析与修复
你的代码存在几个关键问题,直接导致了运行时的段错误,逐一拆解说明:
1. 64位ELF误用32位系统调用指令
你在ELF头里指定了EM_X86_64(64位x86架构),但机器码用的是32位系统调用的int 0x80指令。64位系统环境下执行32位系统调用会破坏寄存器状态,触发异常。必须改用64位架构的syscall指令:
- 64位下
exit的系统调用号是60(而非32位的1) - 64位调用约定:参数存在
rdi寄存器,而非32位的ebx - 正确的指令序列应为:
mov rdi, 0x2a ; 退出码设为42 mov rax, 0x3c ; 指定exit系统调用号60 syscall ; 触发64位系统调用
2. sizeof(programCode)计算错误
programCode是指针类型,sizeof(programCode)在64位系统中返回的是指针长度(8字节),但实际机器码的长度是12字节。你需要用strlen(programCode)(因为机器码中无\0,strlen可正确计算),或者直接用字符串字面量的sizeof减1(减去末尾自动添加的null终止符)。
3. 程序头的偏移与地址对齐不匹配
64位ELF的PT_LOAD段要求p_offset % p_align == p_vaddr % p_align。你设置的p_align是0x1000,p_vaddr是0x401000(模0x1000为0),但p_offset是sizeof(Elf64_Ehdr)+sizeof(Elf64_Phdr)=64+56=120,模0x1000不为0,导致加载器无法正确映射内存,代码会被加载到错误地址,执行时触发段错误。
修复后的代码
#include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <elf.h> #include <string.h> const char programCode[] = "\xbf\x2a\x00\x00\x00" // mov rdi, 0x2a "\xb8\x3c\x00\x00\x00" // mov rax, 0x3c "\x0f\x05"; // syscall int main() { // ELF header Elf64_Ehdr elfHeader = { .e_ident = { ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3, ELFCLASS64, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE, 0, 0, 0, 0, 0, 0, 0 }, .e_type = ET_EXEC, .e_machine = EM_X86_64, .e_version = EV_CURRENT, .e_entry = 0x401000, .e_phoff = 0x1000, // 程序头移到0x1000偏移,满足对齐要求 .e_shoff = 0, .e_flags = 0, .e_ehsize = sizeof(Elf64_Ehdr), .e_phentsize = sizeof(Elf64_Phdr), .e_phnum = 1, .e_shentsize = 0, .e_shnum = 0, .e_shstrndx = 0 }; // Program header Elf64_Phdr programHeader = { .p_type = PT_LOAD, .p_flags = PF_R | PF_X, .p_offset = 0x2000, // 代码段移到0x2000偏移,模0x1000为0 .p_vaddr = 0x401000, .p_paddr = 0, .p_filesz = sizeof(programCode) - 1, // 减去末尾的null终止符 .p_memsz = sizeof(programCode) - 1, .p_align = 0x1000 }; // Create and write the ELF file int fd = open("minimal.elf", O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IXUSR); if (fd == -1) { perror("open"); return 1; } // 写入ELF头 ssize_t written = write(fd, &elfHeader, sizeof(Elf64_Ehdr)); if (written == -1) { perror("write"); close(fd); return 1; } // 填充0到0x1000偏移,准备写程序头 lseek(fd, 0x1000, SEEK_SET); written = write(fd, &programHeader, sizeof(Elf64_Phdr)); if (written == -1) { perror("write"); close(fd); return 1; } // 填充0到0x2000偏移,准备写代码段 lseek(fd, 0x2000, SEEK_SET); written = write(fd, programCode, sizeof(programCode) - 1); if (written == -1) { perror("write"); close(fd); return 1; } close(fd); return 0; }
额外说明
- 修复后的代码将ELF头、程序头、代码段分别放在0x0、0x1000、0x2000偏移,完全满足0x1000的对齐要求
- 改用64位系统调用指令,严格符合x86_64架构的调用约定
- 正确计算机器码长度,避免写入错误的字节数
编译运行后,执行./minimal.elf,再用echo $?查看退出码,会得到42,说明程序正常执行。
内容的提问来源于stack exchange,提问作者Setheron
相关产品推荐
相关产品推荐

