在Linux下编译一个汇编程序需要的工具为 as(汇编器) ld(链接器)
e.g: 一个非常简单的AT&T汇编程序
|
.section .data .section .text .globl _start _start: movl $1, %eax movl $4, %ebx int $0x80 |
.section .globl 这种叫做 伪操作, .section将代码分为若干段
_start 是 symbol(符号) 符号在汇编程序中代表一个地址 .globl 表示一个符号会被链接器使用 as –32 将 汇编代码编译为 relocatable object file elf32_i386
解读ELF 文件格式
对于汇编 , 链接器来看 , elf文件是由 Section Header Table 描述的一系列Section 集合, 而执行一个ELF文件的时候, 在loader看来 它是由 Programer Header Table 描述的一系列Segment的集合
对于程序运行的时候 Program Header Table 是必须
程序链接的时候 Section Header Table 是必须的
对于一个在运行的时候动态链接的程序, Program Header Table 和 Section Header Table 都是需要的, 因为程序既要加载 也要动态链接
ELF文件可以简单分为以下部分:(通过readelf 打印出来的 一个汇编程序的 结构)
汇编代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
.section .data data_items: .long 1,2,3,4,5,6,7,8,9,10,0 .globl _start _start: movl $0, %edi movl data_items(,%edi,4), %eax movl %eax, %ebx start_loop: cmpl $0, %eax je loop_exit incl %edi movl %ebx, %eax jle start_loop movl %eax, %ebx jmp start_loop loop_exit: movl $1, %eax int $0x80 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
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: REL (Relocatable file) Machine: Intel 80386 Version: 0x1 Entry point address: 0x0 Start of program headers: 0 (bytes into file) Start of section headers: 356 (bytes into file) Flags: 0x0 Size of this header: 52 (bytes) Size of program headers: 0 (bytes) Number of program headers: 0 Size of section headers: 40 (bytes) Number of section headers: 8 Section header string table index: 5 |
ELF 文件由ELF Header 和各个Section组成 上面给出的文件是一个 *.o 文件的 ELF Header , 通过ELF Header 可以计算出Section Header 在文件中的位置
|
Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .text PROGBITS 00000000 000034 000023 00 AX 0 0 1 [ 2] .rel.text REL 00000000 00012c 000008 08 I 6 1 4 [ 3] .data PROGBITS 00000000 000057 00002c 00 WA 0 0 1 [ 4] .bss NOBITS 00000000 000083 000000 00 WA 0 0 1 [ 5] .shstrtab STRTAB 00000000 000134 000030 00 0 0 1 [ 6] .symtab SYMTAB 00000000 000084 000080 10 7 7 4 [ 7] .strtab STRTAB 00000000 000104 000028 00 0 0 1 |
上面的内容是Section Header Table中含有的各个段的信息, 因为这是一个 Relocatable Object File(*.o) 还没有被链接, 因此所有的加载到内存中的地址都是 0 , 这里的Offset表示每个Section在 ELF文件中的位置, Size 为每个Section的大小, 通过这些信息,可以将ELF文件的结构做出来 如图所示:

通过hexdump -C 可以查看整个二进制文件的每一位内容, 有了上面的结构图, 可以很容易的和二进制内容对应起来
|
Relocation section '.rel.text' at offset 0x12c contains 1 entries: Offset Info Type Sym.Value Sym. Name 00000008 00000201 R_386_32 00000000 .data The decoding of unwind sections for machine type Intel 80386 is not currently supported. Symbol table '.symtab' contains 8 entries: Num: Value Size Type Bind Vis Ndx Name 0: 00000000 0 NOTYPE LOCAL DEFAULT UND 1: 00000000 0 SECTION LOCAL DEFAULT 1 2: 00000000 0 SECTION LOCAL DEFAULT 3 3: 00000000 0 SECTION LOCAL DEFAULT 4 4: 00000000 0 NOTYPE LOCAL DEFAULT 3 data_items 5: 0000000e 0 NOTYPE LOCAL DEFAULT 1 start_loop 6: 0000001c 0 NOTYPE LOCAL DEFAULT 1 loop_exit 7: 00000000 0 NOTYPE GLOBAL DEFAULT 1 _start |
注意, .bss段中没有任何信息, 在Section 中只占一个Section Header , 没有对应的Section, .shstrtab 用来保存各个Section的名字 .strtab 保存程序中的符号的名字 .rel.text 提供重定位需要的信息, 告诉linker 哪些地方要重定位 .symtab是符号表 Ndx表示这个符号在哪个Section内 Section的编号从 Section Header Table里查到
.text 段保存的就是程序的汇编代码了
Executable ELF Format
上面我们说的都是Relocatable Object File 而不是 Executable File 我们下面来看一下 Object File 在链接之后成为Executable 有什么区别
我们将刚刚的 max.o 编译为 a.out 看ELF Header
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
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: 0x8048074 Start of program headers: 52 (bytes into file) Start of section headers: 476 (bytes into file) Flags: 0x0 Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 2 Size of section headers: 40 (bytes) Number of section headers: 6 Section header string table index: 3 |
这个Header 和 之前的Header没有太大区别 , Type 变为了EXEC, 并且多了Program Header, 下面我们看一下Section Header
|
Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .text PROGBITS 08048074 000074 000023 00 AX 0 0 1 [ 2] .data PROGBITS 08049097 000097 00002c 00 WA 0 0 1 [ 3] .shstrtab STRTAB 00000000 0001b3 000027 00 0 0 1 [ 4] .symtab SYMTAB 00000000 0000c4 0000b0 10 5 7 4 [ 5] .strtab STRTAB 00000000 000174 00003f 00 0 0 1 |
对比前面的可以发现, section header少了一些 , .rel.text .bss 没有了, .rel.text只是用于链接的信息, 链接之后就没有用了 (如果使用 strip 会将符号表symtab 以及 保存符号的Section .strtab也删除, 只保留 .data 和 .shstrtab)
下面看一下多出来的Program Header
|
Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x000000 0x08048000 0x08048000 0x00097 0x00097 R E 0x1000 LOAD 0x000097 0x08049097 0x08049097 0x0002c 0x0002c RW 0x1000 |
两个Program Header 一个是从 0x00 -> 0x97 (通过查看该Program 的Section Header Table 能够看出来 这部分内容就是ELF Header 加上 .text的内容 (根据Size和 Offset来看), 这个被加载到了0x08048000 这个虚拟地址处 (物理地址无意义) 而虽然0x08048000-0x08049097 的空间比 0x00 — 0x97占的空间要大, 但是第二个Segment却没有加载到紧接着它0x08048097之后, 原因是 MMU 的权限保护机制 权限保护是以页面为单位的,也就是说,每一个页面只能设置一种保护权限, 而 .text 以及 ELF Header 的权限是 Read Execute 而 .data段的权限是Read Write 二者权限不同, 应该放在不同的页面内, 内存每一个页面的大小是由Align列表示的, 在x86平台一页为 4K 大小(0x1000) 另外要注意的就是, 每个Segment在文件内的Offset多少,加载到页面内的时候, 要有相同的Offset, 所以第二个Segment加载到了 0x08049097 而不是 0x08049000
以上就是对ELF文件格式的一个简单的解读