ELF概述
ELF (Executable Linkable Format)即可执行可链接文件格式,是 Linux 支持的一种文件存储格式。
ELF 文件类型
ELF 文件的三种类型:
- 可重定位文件(REL,Relocatable ),目标文件或静态库
- 共享目标文件(DYN,Executable ),动态库
- (EXEC),可直接执行的文件
- core文件,吐核文件
| ELF 文件类型 | 说明 | 实例 |
|---|---|---|
| 可重定位文件 (Relocatable File) |
这类文件包含了代码和数据。可以被用来链接成可执行文件或共享目标文件。 | 目标文件 .o 文件和静态库 .a 文件 |
| 可执行文件 (Executable File) |
可直接执行的程序 | /bin/bash 可执行文件 |
| 共享目标文件 (Shared Object File) |
这种文件包含的代码和数据。可以在两种情况下使用,一是链接器使用其与其它可重定位文件、共享目标文件链接,产生新的目标文件。二是动态连接器将其与可执行文件结合,作为进程映像的一部分来运行 | 动态库 .so 文件 |
| 核心转储文件 (Core Dump File) |
core dump 文件 |
可通过 readelf -h 命令查看 ELF 文件头部信息,其中便包含了 ELF 文件类型。如下图所示:
ELF 文件结构
ELF 文件格式定义见 /usr/include/elf.h 。
链接视图
静态链接器(即编译后参与生成最终ELF过程的链接器,如ld )会以链接视图解析ELF。编译时生成的目标文件以及链接后的动态库均可通过链接视图解析,链接视图可以没有段表(如目标文件不会有段表)。
执行视图
动态链接器(即加载器,如x86架构 linux下的 /lib/ld-linux.so.2或者安卓系统下的 /system/linker均为动态链接器)会以执行视图解析ELF并动态链接,执行视图可以没有节表。
ELF 头:Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00类别: ELF64数据: 2 补码,小端序 (little endian)版本: 1 (current)OS/ABI: UNIX - System V程序头:Type Offset VirtAddr PhysAddrFileSiz MemSiz Flags AlignPHDR 0x0000000000000040 0x0000000000400040 0x00000000004000400x00000000000001f8 0x00000000000001f8 R E 8
readelf 命令
readelf 是在类 Unix 系统上显示关于目标文件的各种信息的程序。如其名字所示,它读取 ELF 格式的目标文件。它与 objdump 一起都是 GNU Binutils 的一部分。
常见参数:
- -a,—all 显示全部信息
- -h,—file-header 显示 ELF 文件头
-l,—program-headers 显示程序头(段头)
—segments
-S,—section-headers 显示节头信息
--sections
-s,—syms 显示符号表(.symtab)
- -d,—dynamic 显示动态部分
- —dyn-syms 显示动态符号表(.dynsym)
- -W,—wide 运行显示宽度超过 80 字节
1 文件头
2 节头(section)
readelf -SW
选几个比较重要的进行说明
| 常用段名 | ||
|---|---|---|
| .text | 代码区 | |
| .bss | 未初始化数据区 | |
| .data | 全局初始化数据区 | |
| .line | 行号表 | |
| .plt .got |
动态链接器的跳转表和全局入口表 | Procedure Linkage Table Global Offset Table |
| .init .fini |
程序初始化与终结代码段 | |
| .symtab | 符号表 | |
共有 35 个节头,从偏移量 0x14f0 开始:节头:[Nr] Name Type Address Off Size ES Flg Lk Inf Al[ 0] NULL 0000000000000000 000000 000000 00 0 0 0[ 1] .interp PROGBITS 0000000000400238 000238 00001c 00 A 0 0 1[ 2] .note.ABI-tag NOTE 0000000000400254 000254 000020 00 A 0 0 4[ 3] .note.gnu.build-id NOTE 0000000000400274 000274 000024 00 A 0 0 4[ 4] .gnu.hash GNU_HASH 0000000000400298 000298 000038 00 A 5 0 8[ 5] .dynsym DYNSYM 00000000004002d0 0002d0 000138 18 A 6 1 8[ 6] .dynstr STRTAB 0000000000400408 000408 0000be 00 A 0 0 1[ 7] .gnu.version VERSYM 00000000004004c6 0004c6 00001a 02 A 5 0 2[ 8] .gnu.version_r VERNEED 00000000004004e0 0004e0 000020 00 A 6 1 8[ 9] .rela.dyn RELA 0000000000400500 000500 000018 18 A 5 0 8[10] .rela.plt RELA 0000000000400518 000518 000060 18 A 5 12 8[11] .init PROGBITS 0000000000400578 000578 00001a 00 AX 0 0 4[12] .plt PROGBITS 00000000004005a0 0005a0 000050 10 AX 0 0 16[13] .text PROGBITS 00000000004005f0 0005f0 000182 00 AX 0 0 16[14] .fini PROGBITS 0000000000400774 000774 000009 00 AX 0 0 4[15] .rodata PROGBITS 0000000000400780 000780 000004 04 AM 0 0 4[16] .eh_frame_hdr PROGBITS 0000000000400784 000784 000034 00 A 0 0 4[17] .eh_frame PROGBITS 00000000004007b8 0007b8 0000f4 00 A 0 0 8[18] .init_array INIT_ARRAY 0000000000600df0 000df0 000008 00 WA 0 0 8[19] .fini_array FINI_ARRAY 0000000000600df8 000df8 000008 00 WA 0 0 8[20] .jcr PROGBITS 0000000000600e00 000e00 000008 00 WA 0 0 8[21] .dynamic DYNAMIC 0000000000600e08 000e08 0001f0 10 WA 6 0 8[22] .got PROGBITS 0000000000600ff8 000ff8 000008 08 WA 0 0 8[23] .got.plt PROGBITS 0000000000601000 001000 000038 08 WA 0 0 8[24] .data PROGBITS 0000000000601040 001040 0000a0 00 WA 0 0 32[25] .bss NOBITS 00000000006010e0 0010e0 0000a0 00 WA 0 0 32[26] .comment PROGBITS 0000000000000000 0010e0 00002b 01 MS 0 0 1[27] .debug_aranges PROGBITS 0000000000000000 00110b 000030 00 0 0 1[28] .debug_info PROGBITS 0000000000000000 00113b 0000d9 00 0 0 1[29] .debug_abbrev PROGBITS 0000000000000000 001214 000067 00 0 0 1[30] .debug_line PROGBITS 0000000000000000 00127b 000040 00 0 0 1[31] .debug_str PROGBITS 0000000000000000 0012bb 0000e6 01 MS 0 0 1[32] .shstrtab STRTAB 0000000000000000 0013a1 000148 00 0 0 1[33] .symtab SYMTAB 0000000000000000 001db0 0006d8 18 34 50 8[34] .strtab STRTAB 0000000000000000 002488 000263 00 0 0 1Key to Flags:W (write), A (alloc), X (execute), M (merge), S (strings), l (large)I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)O (extra OS processing required) o (OS specific), p (processor specific)
3 段头/程序头(segment)
程序头描述了 ELF 文件如何别系统映射到进程的虚拟空间。
Elf 文件类型为 EXEC (可执行文件)入口点 0x80484a0共有 9 个程序头,开始于偏移量 52程序头:Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg AlignPHDR 0x000034 0x08048034 0x08048034 0x00120 0x00120 R E 0x4INTERP 0x000154 0x08048154 0x08048154 0x00013 0x00013 R 0x1[Requesting program interpreter: /lib/ld-linux.so.2]LOAD 0x000000 0x08048000 0x08048000 0x0075c 0x0075c R E 0x1000LOAD 0x000ef8 0x08049ef8 0x08049ef8 0x001c8 0x00268 RW 0x1000DYNAMIC 0x000f04 0x08049f04 0x08049f04 0x000f8 0x000f8 RW 0x4NOTE 0x000168 0x08048168 0x08048168 0x00044 0x00044 R 0x4GNU_EH_FRAME 0x000684 0x08048684 0x08048684 0x0002c 0x0002c R 0x4GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x10GNU_RELRO 0x000ef8 0x08049ef8 0x08049ef8 0x00108 0x00108 R 0x1Section to Segment mapping:段节...0001 .interp02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rlt .text .fini .rodata .eh_frame_hdr .eh_frame03 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss04 .dynamic05 .note.ABI-tag .note.gnu.build-id06 .eh_frame_hdr0708 .init_array .fini_array .jcr .dynamic .got
类型为“LOAD”的段是需要被映射的,
符号表
.dynsym 动态符号表保存了与动态链接相关的导入导出符号,不包括模块内部的符号。
.symtab 则保存所有符号,也包括 .dynsym 中的符号。
readelf -sW app 查看符号表
Symbol table '.dynsym' contains 13 entries:Num: Value Size Type Bind Vis Ndx Name0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND1: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTable2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (2)3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND hello4: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__5: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses6: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable7: 0000000000000000 0 FUNC GLOBAL DEFAULT UND sleep@GLIBC_2.2.5 (2)8: 00000000006010e0 0 NOTYPE GLOBAL DEFAULT 24 _edata9: 0000000000601180 0 NOTYPE GLOBAL DEFAULT 25 _end10: 00000000006010e0 0 NOTYPE GLOBAL DEFAULT 25 __bss_start11: 0000000000400578 0 FUNC GLOBAL DEFAULT 11 _init12: 0000000000400774 0 FUNC GLOBAL DEFAULT 14 _finiSymbol table '.symtab' contains 73 entries:Num: Value Size Type Bind Vis Ndx Name0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND1: 0000000000400238 0 SECTION LOCAL DEFAULT 12: 0000000000400254 0 SECTION LOCAL DEFAULT 23: 0000000000400274 0 SECTION LOCAL DEFAULT 34: 0000000000400298 0 SECTION LOCAL DEFAULT 45: 00000000004002d0 0 SECTION LOCAL DEFAULT 56: 0000000000400408 0 SECTION LOCAL DEFAULT 67: 00000000004004c6 0 SECTION LOCAL DEFAULT 78: 00000000004004e0 0 SECTION LOCAL DEFAULT 89: 0000000000400500 0 SECTION LOCAL DEFAULT 910: 0000000000400518 0 SECTION LOCAL DEFAULT 1011: 0000000000400578 0 SECTION LOCAL DEFAULT 1112: 00000000004005a0 0 SECTION LOCAL DEFAULT 1213: 00000000004005f0 0 SECTION LOCAL DEFAULT 1314: 0000000000400774 0 SECTION LOCAL DEFAULT 1415: 0000000000400780 0 SECTION LOCAL DEFAULT 1516: 0000000000400784 0 SECTION LOCAL DEFAULT 1617: 00000000004007b8 0 SECTION LOCAL DEFAULT 1718: 0000000000600df0 0 SECTION LOCAL DEFAULT 1819: 0000000000600df8 0 SECTION LOCAL DEFAULT 1920: 0000000000600e00 0 SECTION LOCAL DEFAULT 2021: 0000000000600e08 0 SECTION LOCAL DEFAULT 2122: 0000000000600ff8 0 SECTION LOCAL DEFAULT 2223: 0000000000601000 0 SECTION LOCAL DEFAULT 2324: 0000000000601040 0 SECTION LOCAL DEFAULT 2425: 00000000006010e0 0 SECTION LOCAL DEFAULT 2526: 0000000000000000 0 SECTION LOCAL DEFAULT 2627: 0000000000000000 0 SECTION LOCAL DEFAULT 2728: 0000000000000000 0 SECTION LOCAL DEFAULT 2829: 0000000000000000 0 SECTION LOCAL DEFAULT 2930: 0000000000000000 0 SECTION LOCAL DEFAULT 3031: 0000000000000000 0 SECTION LOCAL DEFAULT 3132: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c33: 0000000000600e00 0 OBJECT LOCAL DEFAULT 20 __JCR_LIST__34: 0000000000400620 0 FUNC LOCAL DEFAULT 13 deregister_tm_clones35: 0000000000400650 0 FUNC LOCAL DEFAULT 13 register_tm_clones36: 0000000000400690 0 FUNC LOCAL DEFAULT 13 __do_global_dtors_aux37: 00000000006010e0 1 OBJECT LOCAL DEFAULT 25 completed.698238: 0000000000600df8 0 OBJECT LOCAL DEFAULT 19 __do_global_dtors_aux_fini_array_entry39: 00000000004006b0 0 FUNC LOCAL DEFAULT 13 frame_dummy40: 0000000000600df0 0 OBJECT LOCAL DEFAULT 18 __frame_dummy_init_array_entry41: 0000000000000000 0 FILE LOCAL DEFAULT ABS main.c42: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c43: 00000000004008a8 0 OBJECT LOCAL DEFAULT 17 __FRAME_END__44: 0000000000600e00 0 OBJECT LOCAL DEFAULT 20 __JCR_END__45: 0000000000000000 0 FILE LOCAL DEFAULT ABS46: 0000000000600df8 0 NOTYPE LOCAL DEFAULT 18 __init_array_end47: 0000000000600e08 0 OBJECT LOCAL DEFAULT 21 _DYNAMIC48: 0000000000600df0 0 NOTYPE LOCAL DEFAULT 18 __init_array_start49: 0000000000601000 0 OBJECT LOCAL DEFAULT 23 _GLOBAL_OFFSET_TABLE_50: 0000000000400770 2 FUNC GLOBAL DEFAULT 13 __libc_csu_fini51: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTable52: 0000000000601040 0 NOTYPE WEAK DEFAULT 24 data_start53: 00000000006010e0 0 NOTYPE GLOBAL DEFAULT 24 _edata54: 0000000000400774 0 FUNC GLOBAL DEFAULT 14 _fini55: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_2.2.556: 0000000000000000 0 FUNC GLOBAL DEFAULT UND hello57: 0000000000601040 0 NOTYPE GLOBAL DEFAULT 24 __data_start58: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__59: 0000000000601048 0 OBJECT GLOBAL HIDDEN 24 __dso_handle60: 0000000000400780 4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_used61: 0000000000400700 101 FUNC GLOBAL DEFAULT 13 __libc_csu_init62: 0000000000601100 128 OBJECT GLOBAL DEFAULT 25 global_uini_buffer63: 0000000000601180 0 NOTYPE GLOBAL DEFAULT 25 _end64: 00000000004005f0 0 FUNC GLOBAL DEFAULT 13 _start65: 00000000006010e0 0 NOTYPE GLOBAL DEFAULT 25 __bss_start66: 0000000000601060 128 OBJECT GLOBAL DEFAULT 24 global_init_buffer67: 00000000004006dd 21 FUNC GLOBAL DEFAULT 13 main68: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses69: 00000000006010e0 0 OBJECT GLOBAL HIDDEN 24 __TMC_END__70: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable71: 0000000000000000 0 FUNC GLOBAL DEFAULT UND sleep@@GLIBC_2.2.572: 0000000000400578 0 FUNC GLOBAL DEFAULT 11 _init
示例程序中定义了两个全局变量,一个未进行初始化,一个有进行初始化,可以在符号表中找到这两个符号。
char global_uini_buffer[128];char global_init_buffer[128] = {1,2,4};
符号表,其大小和地址在程序启动后是不变的,与表中一致(函数亦然)。
62: 0000000000601100 128 OBJECT GLOBAL DEFAULT 25 global_uini_buffer66: 0000000000601060 128 OBJECT GLOBAL DEFAULT 24 global_init_buffer
节头
[24] .data PROGBITS 0000000000601040 001040 0000a0 00 WA 0 0 32[25] .bss NOBITS 00000000006010e0 0010e0 0000a0 00 WA 0 0 32
根据以上可以得知,global_uini_buffer 位于 .bss 区域, global_init_buffer 位于 .data 区域。同理能查询到函数位于.text 区域。
符号表解读
共计 8 列
- Num: :符号编号,0 - n
- Value :符合地址,16 进制格式
- Size :符号大小,
- Type :符号类型。FUNC 函数,FILE 源文件,OBJECT 变量等
- Bind : 符合绑定。GLOBAL 符号全局可见,LOCAL 符号文件内可见,比如 static 函数,WEAK 全局弱符号,可被覆盖。
- Vis :符号可见性。DEFAULT 缺省。 TODO
- Ndx :符号节头索引。据此可以查看符号所属分区。比如函数符号位域代码区
- Name :符号名称
示例程序
main.c
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include "foo.h"char global_uini_buffer[128];char global_init_buffer[128] = {1,2,4};int main(void){hello();while(1) sleep(1);return 0;}
foo.h
#ifndef __FOO_H__#define __FOO_H__#ifdef __cplusplusextern "C" {#endif#include <unistd.h>#include <stdlib.h>#include <stdio.h>#include <string.h>void hello(void);#ifdef __cplusplus}#endif#endif
foo.c
#include "foo.h"void hello(void){printf("hello world\n");}
补充
- 链接的本质是吧不同的目标文件之间相互“粘”到一起。
- 目标文件之前的相互拼合实际上是其之间对地址的引用,即对函数和变量地址的引用。
- 链接中,将函数和变量统称为符号。
- 每个目标文件都会有一个相应的符号表,记录了目标文件所有用到的符号。每个定义的符号都有一个对应的值,叫做符号值,对于变量和函数,符号值就是它们的地址。
- 定义在本目标文件的全局符号,可以被其它符号
- 本目标文件中引用的全局符号,却没有在本目标文件定义,一般称为外部符号(External Symbol)
- 局部符号,只在编译单元内部可见
- 静态两步链接
- 空间与地址分配
扫描所有输入的目标文件,将各表的符号定义和符号引用合并为一个全局符号表。
- 符号解析与重定位
读取输入文件中数据与重定位信息,进行符号解析与重定位、调制代码中的地址
- API 指源代码级别的接口,ABI 指二进制层面的接口。
- 进程的创建过程
- 创建一个独立的虚拟地址空间
- 读取可执行文件头,并且建立虚拟空间与可执行文件的映射

-
查看进程虚拟空间分布
/proc/
/maps 可查看进程的虚拟空间分布。如下所示, 08048000-08049000 r-xp 00000000 08:01 1971950 /home/admi/tmp/hello_lib/app08049000-0804a000 r--p 00000000 08:01 1971950 /home/admi/tmp/hello_lib/app0804a000-0804b000 rw-p 00001000 08:01 1971950 /home/admi/tmp/hello_lib/appf7569000-f756a000 rw-p 00000000 00:00 0f756a000-f7715000 r-xp 00000000 08:01 819068 /lib/i386-linux-gnu/libc-2.19.sof7715000-f7717000 r--p 001aa000 08:01 819068 /lib/i386-linux-gnu/libc-2.19.sof7717000-f7718000 rw-p 001ac000 08:01 819068 /lib/i386-linux-gnu/libc-2.19.sof7718000-f771b000 rw-p 00000000 00:00 0f7733000-f7734000 rw-p 00000000 00:00 0f7734000-f7735000 r-xp 00000000 08:01 1971941 /home/admi/tmp/hello_lib/libfoo.sof7735000-f7736000 r--p 00000000 08:01 1971941 /home/admi/tmp/hello_lib/libfoo.sof7736000-f7737000 rw-p 00001000 08:01 1971941 /home/admi/tmp/hello_lib/libfoo.sof7737000-f7738000 rw-p 00000000 00:00 0f7738000-f773a000 r--p 00000000 00:00 0 [vvar]f773a000-f773c000 r-xp 00000000 00:00 0 [vdso]f773c000-f775c000 r-xp 00000000 08:01 819065 /lib/i386-linux-gnu/ld-2.19.sof775c000-f775d000 r--p 0001f000 08:01 819065 /lib/i386-linux-gnu/ld-2.19.sof775d000-f775e000 rw-p 00020000 08:01 819065 /lib/i386-linux-gnu/ld-2.19.soffb12000-ffb33000 rw-p 00000000 00:00 0 [stack]
第一列是 虚拟地址空间范围
- 第二列是 权限,r(可读)、w(可写)、x(可执行)、p(私有)、s(共享)
- 第三列是 偏移,该段在映像文件中的偏移
- 第四列是 主次设备号,
- 第五列是 文件节点
- 最后列是 映像文件路径
[stack] 主次设备号和文件节点都是0,表示没有映射到文件中
参考资料
[1] 程序员的自我修养
