ELF 文件操作手册
目录
一、ELF 文件结构概述
二、查看 ELF 文件头信息
1、命令选项
2、示例输出
3、内核数据结构
三、ELF 程序头表
1、命令选项
2、示例输出
3、关键说明
4、内核数据结构
四、ELF 节头表详解
查看节头表信息
1、命令选项
2、示例输出
3、标志说明
4、重要节说明
5、内核数据结构
五、查看节内容
1、反汇编节内容
2、查看目标文件节内容
关键说明
一、ELF 文件结构概述
ELF (Executable and Linkable Format) 是 Unix/Linux 系统中可执行文件、目标文件和共享库的标准文件格式。
二、查看 ELF 文件头信息
1、命令选项
-h
或 --file-header
:显示 ELF 文件的文件头信息。文件头包含了 ELF 文件的基本信息,包括:
-
文件类型
-
机器类型
-
版本信息
-
入口点地址
-
程序头表和节头表的位置和大小等
2、示例输出
$ readelf -h mainELF Header:Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64Data: 2's complement, little endianVersion: 1 (current)OS/ABI: UNIX - System VABI Version: 0Type: EXEC (Executable file)Machine: Advanced Micro Devices X86-64Version: 0x1Entry point address: 0x400640Start of program headers: 64 (bytes into file)Start of section headers: 7048 (bytes into file)Flags: 0x0Size of this header: 64 (bytes)Size of program headers: 56 (bytes)Number of program headers: 9Size of section headers: 64 (bytes)Number of section headers: 31Section header string table index: 30
3、内核数据结构
Linux 内核中 ELF 头部的相关定义(位于 /linux/include/elf.h
):
/* 32位 ELF 头 */
typedef struct elf32_hdr {unsigned char e_ident[EI_NIDENT];Elf32_Half e_type;Elf32_Half e_machine;Elf32_Word e_version;Elf32_Addr e_entry; /* 入口点 */Elf32_Off e_phoff; /* 程序头表偏移 */Elf32_Off e_shoff; /* 节头表偏移 */Elf32_Word e_flags;Elf32_Half e_ehsize;Elf32_Half e_phentsize;Elf32_Half e_phnum;Elf32_Half e_shentsize;Elf32_Half e_shnum;Elf32_Half e_shstrndx;
} Elf32_Ehdr;/* 64位 ELF 头 */
typedef struct elf64_hdr {unsigned char e_ident[EI_NIDENT]; /* ELF 魔数 */Elf64_Half e_type;Elf64_Half e_machine;Elf64_Word e_version;Elf64_Addr e_entry; /* 入口点虚拟地址 */Elf64_Off e_phoff; /* 程序头表文件偏移 */Elf64_Off e_shoff; /* 节头表文件偏移 */Elf64_Word e_flags;Elf64_Half e_ehsize;Elf64_Half e_phentsize;Elf64_Half e_phnum;Elf64_Half e_shentsize;Elf64_Half e_shnum;Elf64_Half e_shstrndx;
} Elf64_Ehdr;
三、ELF 程序头表
1、命令选项
-l
或 --program-headers
:显示 ELF 文件的程序头部(段头)信息。这些信息对于理解可执行文件在内存中的布局和加载过程非常重要。
2、示例输出
$ readelf -l mainElf file type is EXEC (Executable file)
Entry point 0x400640
There are 9 program headers, starting at offset 64Program Headers:Type Offset VirtAddr PhysAddrFileSiz MemSiz Flags AlignPHDR 0x0000000000000040 0x0000000000400040 0x00000000004000400x00000000000001f8 0x00000000000001f8 R E 8INTERP 0x0000000000000238 0x0000000000400238 0x00000000004002380x000000000000001c 0x000000000000001c R 1[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]LOAD 0x0000000000000000 0x0000000000400000 0x00000000004000000x0000000000000d24 0x0000000000000d24 R E 200000LOAD 0x0000000000000e10 0x0000000000600e10 0x0000000000600e100x0000000000000254 0x0000000000000258 RW 200000DYNAMIC 0x0000000000000e28 0x0000000000600e28 0x0000000000600e280x00000000000001d0 0x00000000000001d0 RW 8NOTE 0x0000000000000254 0x0000000000400254 0x00000000004002540x0000000000000044 0x0000000000000044 R 4GNU_EH_FRAME 0x0000000000000b34 0x0000000000400b34 0x0000000000400b340x000000000000005c 0x000000000000005c R 4GNU_STACK 0x0000000000000000 0x0000000000000000 0x00000000000000000x0000000000000000 0x0000000000000000 RW 10GNU_RELRO 0x0000000000000e10 0x0000000000600e10 0x0000000000600e100x00000000000001f0 0x00000000000001f0 R 1Section to Segment mapping:Segment Sections...00 01 .interp 02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame 03 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss 04 .dynamic 05 .note.ABI-tag .note.gnu.build-id 06 .eh_frame_hdr 07 08 .init_array .fini_array .jcr .dynamic .got
3、关键说明
-
PhysAddr:在现代操作系统中,物理地址字段通常可以忽略
-
LOAD:并非所有的节(section)都需要加载到内存,只有标记为 LOAD 的段(segment)才会被加载
4、内核数据结构
/* 32位程序头 */
typedef struct elf32_phdr {Elf32_Word p_type;Elf32_Off p_offset;Elf32_Addr p_vaddr;Elf32_Addr p_paddr;Elf32_Word p_filesz;Elf32_Word p_memsz;Elf32_Word p_flags;Elf32_Word p_align;
} Elf32_Phdr;/* 64位程序头 */
typedef struct elf64_phdr {Elf64_Word p_type;Elf64_Word p_flags;Elf64_Off p_offset; /* 段在文件中的偏移 */Elf64_Addr p_vaddr; /* 段虚拟地址 */Elf64_Addr p_paddr; /* 段物理地址 */Elf64_Xword p_filesz; /* 段在文件中的大小 */Elf64_Xword p_memsz; /* 段在内存中的大小 */Elf64_Xword p_align; /* 段对齐方式 */
} Elf64_Phdr;
四、ELF 节头表详解
查看节头表信息
1、命令选项
-S
或 --section-headers
:显示 ELF 文件的节头信息,包括各节的起始地址、大小、标志等重要属性。
2、示例输出
$ readelf -S mainThere are 31 section headers, starting at offset 0x1b88:Section Headers:[Nr] Name Type Address OffsetSize EntSize Flags Link Info Align[ 0] NULL 0000000000000000 000000000000000000000000 0000000000000000 0 0 0[ 1] .interp PROGBITS 0000000000400238 00000238000000000000001c 0000000000000000 A 0 0 1[ 2] .note.ABI-tag NOTE 0000000000400254 000002540000000000000020 0000000000000000 A 0 0 4[ 3] .note.gnu.build-i NOTE 0000000000400274 000002740000000000000024 0000000000000000 A 0 0 4[ 4] .gnu.hash GNU_HASH 0000000000400298 00000298000000000000001c 0000000000000000 A 5 0 8[ 5] .dynsym DYNSYM 00000000004002b8 000002b80000000000000108 0000000000000018 A 6 1 8[ 6] .dynstr STRTAB 00000000004003c0 000003c00000000000000076 0000000000000000 A 0 0 1[ 7] .gnu.version VERSYM 0000000000400436 000004360000000000000016 0000000000000002 A 5 0 2[ 8] .gnu.version_r VERNEED 0000000000400450 000004500000000000000030 0000000000000000 A 6 1 8[ 9] .rela.dyn RELA 0000000000400480 000004800000000000000018 0000000000000018 A 5 0 8[10] .rela.plt RELA 0000000000400498 0000049800000000000000d8 0000000000000018 AI 5 24 8[11] .init PROGBITS 0000000000400570 00000570000000000000001a 0000000000000000 AX 0 0 4[12] .plt PROGBITS 0000000000400590 0000059000000000000000a0 0000000000000010 AX 0 0 16[13] .plt.got PROGBITS 0000000000400630 000006300000000000000008 0000000000000000 AX 0 0 8[14] .text PROGBITS 0000000000400640 0000064000000000000004b2 0000000000000000 AX 0 0 16[15] .fini PROGBITS 0000000000400af4 00000af40000000000000009 0000000000000000 AX 0 0 4[16] .rodata PROGBITS 0000000000400b00 00000b000000000000000032 0000000000000000 A 0 0 8[17] .eh_frame_hdr PROGBITS 0000000000400b34 00000b34000000000000005c 0000000000000000 A 0 0 4[18] .eh_frame PROGBITS 0000000000400b90 00000b900000000000000194 0000000000000000 A 0 0 8[19] .init_array INIT_ARRAY 0000000000600e10 00000e100000000000000008 0000000000000008 WA 0 0 8[20] .fini_array FINI_ARRAY 0000000000600e18 00000e180000000000000008 0000000000000008 WA 0 0 8[21] .jcr PROGBITS 0000000000600e20 00000e200000000000000008 0000000000000000 WA 0 0 8[22] .dynamic DYNAMIC 0000000000600e28 00000e2800000000000001d0 0000000000000010 WA 6 0 8[23] .got PROGBITS 0000000000600ff8 00000ff80000000000000008 0000000000000008 WA 0 0 8[24] .got.plt PROGBITS 0000000000601000 000010000000000000000060 0000000000000008 WA 0 0 8[25] .data PROGBITS 0000000000601060 000010600000000000000004 0000000000000000 WA 0 0 1[26] .bss NOBITS 0000000000601064 000010640000000000000004 0000000000000000 WA 0 0 1[27] .comment PROGBITS 0000000000000000 00001064000000000000002d 0000000000000001 MS 0 0 1[28] .symtab SYMTAB 0000000000000000 000010980000000000000750 0000000000000018 29 49 8[29] .strtab STRTAB 0000000000000000 000017e80000000000000291 0000000000000000 0 0 1[30] .shstrtab STRTAB 0000000000000000 00001a79000000000000010c 0000000000000000 0 0 1
3、标志说明
- W (write) - 可写
- A (alloc) - 运行时分配内存
- X (execute) - 可执行
- M (merge) - 可合并
- S (strings) - 包含字符串
- I (info) - 额外信息
- L (link order) - 保留链接顺序
- O (extra OS processing required) - 需要特殊处理
- G (group) - 组成员
- T (TLS) - 线程局部存储
- C (compressed) - 压缩数据
- x (unknown) - 未知
- o (OS specific) - 操作系统特定
- E (exclude) - 排除
- l (large) - 大型节
- p (processor specific) - 处理器特定
4、重要节说明
-
.got:全局偏移表(Global Offset Table),用于动态链接
-
.plt:过程链接表(Procedure Linkage Table),支持延迟绑定
-
.text:包含程序的可执行指令
-
.data:包含初始化的全局变量
-
.bss:包含未初始化的全局变量
5、内核数据结构
/* 32位节头 */
typedef struct {Elf32_Word sh_name; /* 节名称索引 */Elf32_Word sh_type; /* 节类型 */Elf32_Word sh_flags; /* 节标志 */Elf32_Addr sh_addr; /* 节虚拟地址 */Elf32_Off sh_offset; /* 节文件偏移 */Elf32_Word sh_size; /* 节大小 */Elf32_Word sh_link; /* 相关节索引 */Elf32_Word sh_info; /* 附加信息 */Elf32_Word sh_addralign; /* 节对齐要求 */Elf32_Word sh_entsize; /* 表项大小(如有) */
} Elf32_Shdr;/* 64位节头 */
typedef struct elf64_shdr {Elf64_Word sh_name; /* 节名称索引 */Elf64_Word sh_type; /* 节类型 */Elf64_Xword sh_flags; /* 节标志 */Elf64_Addr sh_addr; /* 节虚拟地址 */Elf64_Off sh_offset; /* 节文件偏移 */Elf64_Xword sh_size; /* 节大小 */Elf64_Word sh_link; /* 相关节索引 */Elf64_Word sh_info; /* 附加信息 */Elf64_Xword sh_addralign; /* 节对齐要求 */Elf64_Xword sh_entsize; /* 表项大小(如有) */
} Elf64_Shdr;
五、查看节内容
1、反汇编节内容
$ objdump -S mainmain: file format elf64-x86-64Disassembly of section .init:0000000000400570 <_init>:400570: 48 83 ec 08 sub $0x8,%rsp400574: 48 8b 05 7d 0a 20 00 mov 0x200a7d(%rip),%rax # 600ff8 <__gmon_start__>40057b: 48 85 c0 test %rax,%rax40057e: 74 05 je 400585 <_init+0x15>400580: e8 ab 00 00 00 callq 400630 <.plt.got>400585: 48 83 c4 08 add $0x8,%rsp400589: c3 retqDisassembly of section .plt:0000000000400590 <.plt>:400590: ff 35 72 0a 20 00 pushq 0x200a72(%rip) # 601008 <_GLOBAL_OFFSET_TABLE_+0x8>400596: ff 25 74 0a 20 00 jmpq *0x200a74(%rip) # 601010 <_GLOBAL_OFFSET_TABLE_+0x10>40059c: 0f 1f 40 00 nopl 0x0(%rax)00000000004005a0 <write@plt>:4005a0: ff 25 72 0a 20 00 jmpq *0x200a72(%rip) # 601018 <write@GLIBC_2.2.5>4005a6: 68 00 00 00 00 pushq $0x04005ab: e9 e0 ff ff ff jmpq 400590 <.plt>00000000004005b0 <printf@plt>:4005b0: ff 25 6a 0a 20 00 jmpq *0x200a6a(%rip) # 601020 <printf@GLIBC_2.2.5>4005b6: 68 01 00 00 00 pushq $0x14005bb: e9 d0 ff ff ff jmpq 400590 <.plt>00000000004005c0 <close@plt>:4005c0: ff 25 62 0a 20 00 jmpq *0x200a62(%rip) # 601028 <close@GLIBC_2.2.5>4005c6: 68 02 00 00 00 pushq $0x24005cb: e9 c0 ff ff ff jmpq 400590 <.plt>00000000004005d0 <__libc_start_main@plt>:4005d0: ff 25 5a 0a 20 00 jmpq *0x200a5a(%rip) # 601030 <__libc_start_main@GLIBC_2.2.5>4005d6: 68 03 00 00 00 pushq $0x34005db: e9 b0 ff ff ff jmpq 400590 <.plt>
2、查看目标文件节内容
$ objdump -d hello.ohello.o: file format elf64-x86-64Disassembly of section .text:0000000000000000 <main>:0: 55 push %rbp1: 48 89 e5 mov %rsp,%rbp4: bf 00 00 00 00 mov $0x0,%edi9: e8 00 00 00 00 callq e <main+0xe>e: b8 00 00 00 00 mov $0x0,%eax13: e8 00 00 00 00 callq 18 <main+0x18>18: b8 00 00 00 00 mov $0x0,%eax1d: 5d pop %rbp1e: c3 retq
关键说明
-
.init:程序初始化代码
-
.plt:动态链接跳转表
-
.text:主程序代码
-
目标文件中的地址在链接前为0,链接后会填充实际地址