修复手动构建ELF文件时"output has a corrupt string table index"警告
错误核心原因
你写的ELF结构完全不符合ELF可重定位文件的规范,几个致命错误直接触发字符串表损坏报错:
- 没有构造节名字符串表(.shstrtab),但ELF头里
e_shstrndx = 1声明节名表的节头索引为1,实际根本不存在对应节 - 节头数量配置完全错误:ELF规范要求第一个节头必须是全0的NULL节头,你现在
e_shnum = 1注释却写有2个节头,实际也没有预留NULL节头、.shstrtab节头的位置 .text节的sh_name赋值错误:sh_name是节名字符串在.shstrtab中的偏移,你误用了动态标签宏DT_TEXTREL,完全不相关- 代码段长度错误:你给出的汇编指令只有12字节,定义
code[13]会多写入一个无效的\0到代码段 - 写入文件时缺了NULL节头、.shstrtab节头、.shstrtab字符串内容三个关键部分
修复方案
按照规范补全缺失的ELF结构即可,修改后的完整代码如下:
#include <iostream> #include <elf.h> #include <cstdio> #include <cstring> int main() { // 1. 构造节名字符串表 // 结构:0字节留空 + ".shstrtab" + '\0' + ".text" + '\0' const char shstrtab[] = "\0.shstrtab\0.text"; const int shstrtab_size = sizeof(shstrtab); // 2. ELF头 Elf64_Ehdr ELFheader = {0}; ELFheader.e_ident[EI_MAG0] = ELFMAG0; ELFheader.e_ident[EI_MAG1] = ELFMAG1; ELFheader.e_ident[EI_MAG2] = ELFMAG2; ELFheader.e_ident[EI_MAG3] = ELFMAG3; ELFheader.e_ident[EI_CLASS] = ELFCLASS64; ELFheader.e_ident[EI_DATA] = ELFDATA2LSB; ELFheader.e_ident[EI_VERSION] = EV_CURRENT; ELFheader.e_ident[EI_OSABI] = ELFOSABI_SYSV; ELFheader.e_ident[EI_ABIVERSION] = 0; ELFheader.e_type = ET_REL; ELFheader.e_machine = EM_X86_64; ELFheader.e_version = EV_CURRENT; ELFheader.e_entry = 0; ELFheader.e_phoff = 0; ELFheader.e_shoff = sizeof(Elf64_Ehdr); // 节头紧跟ELF头 ELFheader.e_flags = 0; ELFheader.e_ehsize = sizeof(Elf64_Ehdr); ELFheader.e_phentsize = 0; ELFheader.e_phnum = 0; ELFheader.e_shentsize = sizeof(Elf64_Shdr); ELFheader.e_shnum = 3; // 共3个节头:NULL、.shstrtab、.text ELFheader.e_shstrndx = 1; // 节名表的节头索引是1 // 3. 节头表:共3个 Elf64_Shdr shdrs[3] = {0}; // 第一个是必须的NULL节头,全0即可 // 第二个是.shstrtab节头 shdrs[1].sh_name = 1; // ".shstrtab"在字符串表的偏移是1 shdrs[1].sh_type = SHT_STRTAB; shdrs[1].sh_flags = 0; shdrs[1].sh_addr = 0; shdrs[1].sh_offset = sizeof(Elf64_Ehdr) + 3 * sizeof(Elf64_Shdr); // 紧跟节头表之后 shdrs[1].sh_size = shstrtab_size; shdrs[1].sh_link = 0; shdrs[1].sh_info = 0; shdrs[1].sh_addralign = 1; shdrs[1].sh_entsize = 0; // 第三个是.text节头 shdrs[2].sh_name = 11; // ".text"在字符串表的偏移是11 shdrs[2].sh_type = SHT_PROGBITS; shdrs[2].sh_flags = SHF_EXECINSTR | SHF_ALLOC; shdrs[2].sh_addr = 0; shdrs[2].sh_offset = shdrs[1].sh_offset + shstrtab_size; // 紧跟.shstrtab之后 shdrs[2].sh_size = 12; // 实际指令长度是12字节 shdrs[2].sh_link = 0; shdrs[2].sh_info = 0; shdrs[2].sh_addralign = 16; shdrs[2].sh_entsize = 0; // 4. 代码段:12字节的exit(2)系统调用 unsigned char code[12] = "\xb8\x3c\x00\x00\x00\xbf\x02\x00\x00\x00\x0f\x05"; // 5. 一次性写入所有内容,避免多次追加偏移出错 FILE *fout = fopen("output.o", "wb"); fwrite(&ELFheader, sizeof(ELFheader), 1, fout); fwrite(shdrs, sizeof(shdrs), 1, fout); fwrite(shstrtab, shstrtab_size, 1, fout); fwrite(code, sizeof(code), 1, fout); fclose(fout); return 0; }
验证步骤
- 编译生成器:
g++ elf_generator.cpp -o elf_generator - 运行生成.o文件:
./elf_generator - 查看汇编:
objdump -d output.o,可以正常看到exit系统调用的汇编代码 - 链接生成可执行文件:
ld output.o -o test_exec - 运行验证:
./test_exec,执行echo $?会输出2,符合预期。
内容的提问来源于stack exchange,提问作者Ari
相关产品推荐
相关产品推荐

