作者:曹忠明,华清远见嵌入式学院讲师。
前些天看了一些关于ELF的文章,写了这个东西,算是一个笔记吧!
ELF(Executable and linking format)是一种二进制格式,在一些linux或Unix中作为默认的可执行格式。
ELF有三种主要的文件类型
1、 可执行文件:包含代码和数据。可以执行的文件。如下:
$gcc test.c –o test
$file test
test: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.6.9, dynamically linked (uses shared libs), for GNU/Linux 2.6.9, not stripped
2、 可重定位文件:就是我们说的目标文件,没有连接之前的。如下:
$gcc –c test.c
$file test.o
test.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped
3、共享0bject文件:就是共享库,这些文件中的数据和代码在连接的时候和运行时动态加载。如下:
$file /lib/libc-2.5.so
/lib/libc-2.5.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), for GNU/Linux 2.6.9, not stripped
ELF文件格式为:
ELF header(ELF头部) |
Program header table(程序头表) |
Segment1(段1) |
Segment2(段2) |
……… |
Sengmentn(段n) |
Setion header table(节头表,可选 |
每个段由若干个节组成,节头表对每个节的信息有相关的描述,对可执行程序而言节头表是可选的。下面是一个ELF头的数据结构(/usr/include/elf.h可以查看)。
typedef struct
{
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; /* ELF头部长度 */
Elf32_Half e_phentsize; /* 程序头部中一个条目的长度 */
Elf32_Half e_phnum; /* 程序头部条目个数 */
Elf32_Half e_shentsize; /* 节头部中一个条目的长度 */
Elf32_Half e_shnum; /* 节头部条目个数 */
Elf32_Half e_shstrndx; /* 节头部字符表索引 */
} Elf32_Ehdr;
通过readelf可以查看这些信息
$readelf –h test
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x80482b0
Start of program headers: 52 (bytes into file)
Start of section headers: 1988 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 7
Size of section headers: 40 (bytes)
Number of section headers: 28
Section header string table index: 25
这里简单的说明一下这些数据的意义:
Magic对应结构体Elf32_Ehdr中的e_ident[EI_NIDENT],e_ident[0] 到e_ident[3]分别为7f 45 4c 46 把这几个数据转换为字符7f是不可打印字符 45 = ‘E’ ,4c = ‘L’, 46 = ‘F’这里就说这是一个ELF文件,e_ident[4] 表示硬件系统的位数,1代表32位,2代表64位。e_ident[5]表示数据的字节序1表示小端排序,2表示大端排序。e_ident[6]表示ELF头的版本。其余段的意义都可以从Elf32_ehdr的定义查看。
在ELF头部是程序表头,他是一个结构体数组,它包含ELF头表中字段e_phnum所表示的字段。他的结构如下(/usr/include/elf.h可以查看)
typedef struct {
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;
可以通过readelf查看这些程序表头
$readelf –S test
There are 28 section headers, starting at offset 0x7c4:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 08048114 000114 000013 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 08048128 000128 000020 00 A 0 0 4
[ 3] .gnu.hash GNU_HASH 08048148 000148 000020 04 A 4 0 4
[ 4] .dynsym DYNSYM 08048168 000168 000050 10 A 5 1 4
[ 5] .dynstr STRTAB 080481b8 0001b8 00004c 00 A 0 0 1
[ 6] .gnu.version VERSYM 08048204 000204 00000a 02 A 4 0 2
[ 7] .gnu.version_r VERNEED 08048210 000210 000020 00 A 5 1 4
[ 8] .rel.dyn REL 08048230 000230 000008 08 A 4 0 4
[ 9] .rel.plt REL 08048238 000238 000018 08 A 4 11 4
[10] .init PROGBITS 08048250 000250 000017 00 AX 0 0 4
[11] .plt PROGBITS 08048268 000268 000040 04 AX 0 0 4
[12] .text PROGBITS 080482b0 0002b0 0001d8 00 AX 0 0 16
[13] .fini PROGBITS 08048488 000488 00001c 00 AX 0 0 4
[14] .rodata PROGBITS 080484a4 0004a4 00002f 00 A 0 0 4
[15] .eh_frame PROGBITS 080484d4 0004d4 000004 00 A 0 0 4
[16] .ctors PROGBITS 080494d8 0004d8 000008 00 WA 0 0 4
[17] .dtors PROGBITS 080494e0 0004e0 000008 00 WA 0 0 4
[18] .jcr PROGBITS 080494e8 0004e8 000004 00 WA 0 0 4
[19] .dynamic DYNAMIC 080494ec 0004ec 0000c8 08 WA 5 0 4
[20] .got PROGBITS 080495b4 0005b4 000004 04 WA 0 0 4
[21] .got.plt PROGBITS 080495b8 0005b8 000018 04 WA 0 0 4
[22] .data PROGBITS 080495d0 0005d0 000004 00 WA 0 0 4
[23] .bss NOBITS 080495d4 0005d4 000008 00 WA 0 0 4
[24] .comment PROGBITS 00000000 0005d4 000114 00 0 0 1
[25] .shstrtab STRTAB 00000000 0006e8 0000db 00 0 0 1
[26] .symtab SYMTAB 00000000 000c24 000440 10 27 48 4
[27] .strtab STRTAB 00000000 001064 00025a 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
对一个ELF可执行程序而言,一个基本的段是标记p_type为PT_INTERP的段,它表明了运行此程序所需要的程序解释器(/lib/ld-linux.so.2),实际上也就是动态连接器(dynamic linker)。最重要的段是标记p_type为PT_LOAD的段,它表明了为运行程序而需要加载到内存的数据。查看上面实际输入,可以看见有两个可LOAD段,第一个为只读可执行(FLg为R E),第二个为可读可写(Flg为RW)。段1包含了文本节.text,注意到ELF文件头部中程序进入点的值为0x80483cc,正好是指向节.text在内存中的地址。段二包含了数据节.data,此数据节中数据是可读可写的,相对的只读数据节.rodata包含在段1中。
文章评论(0条评论)
登录后参与讨论