原创 也说说ELF

2010-1-13 14:47 2120 3 3 分类: MCU/ 嵌入式

 


作者:曹忠明,华清远见嵌入式学院讲师。


前些天看了一些关于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条评论)

登录后参与讨论
我要评论
0
3
关闭 站长推荐上一条 /2 下一条