ELF 是洋文Executable and Linking Forma
的缩写,即可执行可连接格式,具有这种格式的文件成为ELF文件
在 ELF 规范中,所有符合 ELF 格式规范的都称为 ELF 文件,也称为目标文件
主要分为三种类型
可重定位文件
用于与其它目标文件进行连接以构建可执行文件或动态链接库
可重定位文件引用其他目标文件中的符号时,只给出一个名字而没有地址,需要在链接时进行重定位,所以称为可重定位文件
例
//main.c
#include <stdio.h>
int main(int argc, char const *argv[])
{
fuc();
return 0;
}
//func.c
void fuc()
{
puts("A1_D4i");
}
gcc -c main.c -o main.o
gcc -c fuc.c -o fuc.o
file main.o
main.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
file fuc.o
fuc.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
共享目标文件
即动态连接库文件
使用情况有两种
例
file libc-2.27.so
libc-2.27.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/l, BuildID[sha1]=b417c0ba7cc5cf06d1d1bed6652cedb9253c60d0, for GNU/Linux 3.2.0, stripped
共享目标文件是可执行的
./libc-2.27.so
GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1) stable release version 2.27.
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 7.3.0.
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
<https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>
可执行文件
经过链接的,可以执行的程序文件
例
gcc main.o fuc.o test
file test
test: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=6ec6ce6732da07955bd3a4aceabaa43e7f3f620b, not stripped
./test
A1_D4i
//hello.c
#include <stdio.h>
int main(int argc, char const *argv[])
{
puts("hello world");
return 0;
}
gcc hello.c -o hello1
file hello1
hello1: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=fd4ebdec6f6339fcdd99a594cd6b62180fcc375c, not stripped
生成了ELF 64-bit LSB shared object而不是ELF 64-bit LSB executable
gcc hello.c -o hello2 -no-pie
file hello2
hello2: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=5677faa90a57d5665abefef0a3a2137ee0c97b82, not stripped
关闭pie,生成了ELF 64-bit LSB executable
Position-Independent-Executable是Binutils,glibc和gcc的一个功能,能用来创建介于共享库和通常可执行代码之间的代码–能像共享库一样可重分配地址的程序,这种程序必须连接到Scrt1.o。标准的可执行程序需要固定的地址,并且只有被装载到这个地址时,程序才能正确执行。PIE能使程序像共享库一样在主存任何位置装载,这需要将程序编译成位置无关,并链接为ELF共享对象。
#define EI_NIDENT 16
typedef struct
{
unsigned char e_ident[EI_NIDENT];
Elf_Half e_type;
Elf_Half e_machine;
Elf_Word e_version;
Elf_Addr e_entry;
Elf_Off e_phoff;
Elf_Off e_shoff;
Elf_Word e_flags;
Elf_Half e_ehsize;
Elf_Half e_phentsize;
Elf_Half e_phnum;
Elf_Half e_shentsize;
Elf_Half e_shnum;
Elf_Half e_shstrndx;
} Elf_Ehdr;
readelf -h查看ELF Header
readelf -h hello2
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x400400
Start of program headers: 64 (bytes into file)
Start of section headers: 6376 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 9
Size of section headers: 64 (bytes)
Number of section headers: 29
Section header string table index: 28
e_ident[EI_NIDENT]
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
16字节
e_ident[0] - e_ident[3]为Magic number
'\x7fELF'
e_ident[4]是class,指明文件类型
Class: ELF64
e_ident[5]是data,指明编码格式
Data: 2's complement, little endian
e_ident[6]是version,指明elf文件头的版本
Version: 1 (current)
e_ident[7]-e_ident[14]为pad,通常为0
e_type
ELF文件类型
Type: EXEC (Executable file)
e_machine
指定适用的处理器体系结构
Machine: Advanced Micro Devices X86-64
e_version
指明目标文件的版本
Version: 0x1
e_entry
指明程序入口的虚拟地址,即当文件被加载到进程空间里后,入口程序在进程地址空间里的地址
Entry point address: 0x400400
e_phof
指明程序头表开始处在文件中的偏移量。如果没有程序头表,该值应设为0
Start of program headers: 64 (bytes into file)
e_shoff
指明节头表开始处在文件中的偏移量。如果没有节头表,该值应设为0
Start of section headers: 6376 (bytes into file)
e_flags
含有处理器特定的标志位。标志的名字符合”EF_machine_flag”的格式
对于 Intel 架构的处理器来说,它没有定义任何标志位,所以 e_flags 应该为0
Flags: 0x0
e_ehsize
指明 ELF 文件头的大小,以字节为单位
Size of this header: 64 (bytes)
e_phentsize
指明在程序头表中每一个表项的大小,以字节为单位
Size of program headers: 56 (bytes)
e_phnum
指明程序头表中总共有多少个表项
如果一个目标文件中没有程序头表,该值应设为0
Number of program headers: 9
e_shentsize 指明在节头表中每一个表项的大小,以字节为单位
Size of section headers: 64 (bytes)
e_shnum 指明节头表中总共有多少个表项
如果一个目标文件中没有节头表,该值应设为0
Number of section headers: 29
e_shstrndx 指明节头表中与节名字表相对应的表项的索引
如果文件没有节名字表,此值应设置为SHN_UNDEF
Section header string table index: 28
所有节都登记在一张称为节头表的数组里
节头表的每一个表项是一个 Elf_Shdr结构,通过每一个表项可以定位到对应的节
typedef struct {
Elf_Word sh_name;
Elf_Word sh_type;
Elf_Word sh_flags;
Elf_Addr sh_addr;
Elf_Off sh_offset;
Elf_Word sh_size;
Elf_Word sh_link;
Elf_Word sh_info;
Elf_Word sh_addralign;
Elf_Word sh_entsize;
} Elf_Shdr;
Number of section headers: 29
Size of section headers: 64 (bytes)
readelf --section-headers hello2
There are 29 section headers, starting at offset 0x18e8:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 0000000000400238 00000238
000000000000001c 0000000000000000 A 0 0 1
[ 2] .note.ABI-tag NOTE 0000000000400254 00000254
0000000000000020 0000000000000000 A 0 0 4
[ 3] .note.gnu.build-i NOTE 0000000000400274 00000274
0000000000000024 0000000000000000 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0000000000400298 00000298
000000000000001c 0000000000000000 A 5 0 8
[ 5] .dynsym DYNSYM 00000000004002b8 000002b8
0000000000000060 0000000000000018 A 6 1 8
[ 6] .dynstr STRTAB 0000000000400318 00000318
000000000000003d 0000000000000000 A 0 0 1
[ 7] .gnu.version VERSYM 0000000000400356 00000356
0000000000000008 0000000000000002 A 5 0 2
[ 8] .gnu.version_r VERNEED 0000000000400360 00000360
0000000000000020 0000000000000000 A 6 1 8
[ 9] .rela.dyn RELA 0000000000400380 00000380
0000000000000030 0000000000000018 A 5 0 8
[10] .rela.plt RELA 00000000004003b0 000003b0
0000000000000018 0000000000000018 AI 5 22 8
[11] .init PROGBITS 00000000004003c8 000003c8
0000000000000017 0000000000000000 AX 0 0 4
[12] .plt PROGBITS 00000000004003e0 000003e0
0000000000000020 0000000000000010 AX 0 0 16
[13] .text PROGBITS 0000000000400400 00000400
0000000000000182 0000000000000000 AX 0 0 16
[14] .fini PROGBITS 0000000000400584 00000584
0000000000000009 0000000000000000 AX 0 0 4
[15] .rodata PROGBITS 0000000000400590 00000590
0000000000000010 0000000000000000 A 0 0 4
[16] .eh_frame_hdr PROGBITS 00000000004005a0 000005a0
000000000000003c 0000000000000000 A 0 0 4
[17] .eh_frame PROGBITS 00000000004005e0 000005e0
0000000000000100 0000000000000000 A 0 0 8
[18] .init_array INIT_ARRAY 0000000000600e10 00000e10
0000000000000008 0000000000000008 WA 0 0 8
[19] .fini_array FINI_ARRAY 0000000000600e18 00000e18
0000000000000008 0000000000000008 WA 0 0 8
[20] .dynamic DYNAMIC 0000000000600e20 00000e20
00000000000001d0 0000000000000010 WA 6 0 8
[21] .got PROGBITS 0000000000600ff0 00000ff0
0000000000000010 0000000000000008 WA 0 0 8
[22] .got.plt PROGBITS 0000000000601000 00001000
0000000000000020 0000000000000008 WA 0 0 8
[23] .data PROGBITS 0000000000601020 00001020
0000000000000010 0000000000000000 WA 0 0 8
[24] .bss NOBITS 0000000000601030 00001030
0000000000000008 0000000000000000 WA 0 0 1
[25] .comment PROGBITS 0000000000000000 00001030
000000000000002b 0000000000000001 MS 0 0 1
[26] .symtab SYMTAB 0000000000000000 00001060
00000000000005b8 0000000000000018 27 43 8
[27] .strtab STRTAB 0000000000000000 00001618
00000000000001c9 0000000000000000 0 0 1
[28] .shstrtab STRTAB 0000000000000000 000017e1
0000000000000103 0000000000000000 0 0 1
Key to Flags:
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)
sh_name
本节的名字
名字的字符串并不储存在这里,只是一个索引号,指向字符串表节中的某个位置
sh_type
指明本节的类型
所有的节类型如下
SHT_NULL
表明本节头是一个无效的(非活动的)节头,它也没有对应的节
本节头中的其它成员的值也都是没有意义的
SHT_PROGBITS
表明本节所含有的信息是由程序定义的
本节内容的格式和含义都由程序来决定
SHT_SYMTAB/SHT_DYNSYM
这两类节都含有符号表
目前,目标文件中最多只能各包含一个
SHT_STRTAB
表明本节是字符串表节
SHT_HASH
表明本节包含一张哈希表
目前,一个目标文件中最多只能有一张哈希表
SHT_DYNAMIC
表明本节包含的是动态连接信息
目前,一个目标文件中最多只能有一个 SHT_DYNAMIC 节
SHT_NOTE
表明本节包含的信息用于以某种方式来标记本文件
SHT_NOBITS
表明本节的内容是空的,节并不占用实际的空间
SHT_REL
表明本节是一个重定位节
SHT_SHLIB
此值是一个保留值,暂未指定语义
SHT_DYNSYM
表明本节是符号表,与 SHT_SYMTAB 同义
SHT_LOPROC
为特殊处理器保留的节类型索引值的下边界
SHT_HIPROC
为特殊处理器保留的节类型索引值的上边界
SHT_LOPROC ~ SHT_HIPROC区间是为特殊处理器节类型的保留值
SHT_LOUSER
为应用程序保留节类型索引值的下边界
为应用程序保留节类型索引值的下边界
SHT_LOUSER ~ SHT_HIUSER 区间的节类型可由应用程序自行定义,是一段保留值
sh_flags
指明本节的属性
当某种属性被设置时,相应标志位设为1,反之设为0
未定义标志位全部设为0
SHF_WRITE
如果此标志被设置,表示本节所包含的内容在进程运行过程中是可写的
SHF_ALLOC
如果此标志被设置,表示本节内容在进程运行过程中要占用内存单元
SHF_EXECINSTR
如果此标志被设置,表示此节内容是指令代码
SHF_MASKPROC
所有被此值所覆盖的位都是保留做特殊处理器扩展用的
sh_addr
如果本节的内容需要映射到进程空间中去,指明映射的起始地址
如果不需要映射,此值为0
sh_offset
指明本节所在的位置
该值是节的第一个字节相对于文件开头的偏移量,单位是字节
sh_size
指明节的大小,单位是字节
节类型为SHT_NOBITS时此值仍然可能为非零,但没有实际的意义
sh_link
索引值,指向节头表中本节所对应的位置
sh_info
此节的附加信息
以.
为前缀的节名字是为系统保留的
.bss
包含目标文件中未初始化的全局变量
一般情况下,可执行程序在开始运行的时候,系统会把这一段内容清零
在运行期间的 bss 段是由系统初始化而成的,在目标文件中.bss 节并不包含任何内容,其长度为 0,所以它的节类型为 SHT_NOBITS
.comment
包含版本控制信息
.data/.data1
存放程序中被初始化过的全局变量
.debug
含有调试信息
.dynamic
包含动态连接信息
.dynstr
含有用于动态连接的字符串
.dynsym
节含有动态连接符号表
fini
此节包含进程终止时要执行的程序指令。当程序正常退出时,系统会执行这一节中的代码
.got
包含全局偏移表
.hash
包含符号哈希表
.init
包含进程初始化时要执行的程序指令。当程序开始运行时,系统会在进入主函数之前执行这一节中的代码
.interp
节含有 ELF 程序解析器的路径名
.line
包含些调试符号的行号,为程序指令码与源文件的行号建立起联系
.note
注释节
.plt
包含进程链接表
.relname/.relaname
包含重定位信息
.rodata/.rodata1
包含程序中的只读数据
.shstrtab
含有所有其它节的名字
.strtab
存放字符串,主要是符号表项的名字
.symtab
存放符号表
.text
包含程序指令代码
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议(CC BY-NC-ND 4.0)进行许可。
This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License (CC BY-NC-ND 4.0).