摘要:本文主要为你解释一个C文件是如何被一步步处理成可执行的elf格式文件的。
本文来源: 从C文件到ELF
说明:所有本文的用例是以下hello.c程序:
#include<stdio.h> int main(int argc, char *argv[]) { printf("hello world\n"); return 0; }
1.预处理
我们来看看hello.c经过预处理以后的结果:gcc -E hello.c -o hello.i
extern int ftrylockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ; extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)); # 940 "/usr/include/stdio.h" 3 4 # 2 "hello.c" 2 int main(int argc, char *argv[]) { printf("hello world\n"); return 0; }
变化:预处理结果就是将stdio.h 文件中的内容插入到hello.c中了,文件变成了855行
2编译为汇编代码(Compilation)
编译:编译器的作用是预处理之后,可直接对生成的hello.i文件编译,生成汇编代码:
gcc -S hello.i -o hello.s
gcc的-S选项,表示在程序编译期间,在生成汇编代码后,停止,-o输出汇编代码文件。我们同样可以用vim打开观看:
1 .file "hello.c" 2 .section .rodata 3 .LC0: 4 .string "hello world" 5 .text 6 .globl main 7 .type main, @function 8 main: 9 .LFB0: 10 .cfi_startproc 11 pushl %ebp 12 .cfi_def_cfa_offset 8 13 .cfi_offset 5, -8 14 movl %esp, %ebp 15 .cfi_def_cfa_register 5 16 andl $-16, %esp 17 subl $16, %esp 18 movl $.LC0, (%esp) 19 call puts 20 movl $0, %eax 21 leave 22 .cfi_restore 5 23 .cfi_def_cfa 4, 4 24 ret 25 .cfi_endproc 26 .LFE0: 27 .size main, .-main 28 .ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3" 29 .section .note.GNU-stack,"",@progbits
3汇编(Assembly)
汇编:汇编器对于上一小节中生成的汇编代码文件hello.s,gas汇编器负责将其编译为目标文件,如下:
gcc -c hello.s -o hello.o
说明,这一步将程序划分为若干段(数据段,代码段等),可以用objdump命令来查看这些目标文件的内容。
$ objdump -x hello.o hello.o: file format elf32-i386 hello.o architecture: i386, flags 0x00000011: HAS_RELOC, HAS_SYMS start address 0x00000000 Sections: Idx Name Size VMA LMA File off Algn 0 .text 0000001c 00000000 00000000 00000034 2**2 CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE 1 .data 00000000 00000000 00000000 00000050 2**2 CONTENTS, ALLOC, LOAD, DATA 2 .bss 00000000 00000000 00000000 00000050 2**2 ALLOC 3 .rodata 0000000c 00000000 00000000 00000050 2**0 CONTENTS, ALLOC, LOAD, READONLY, DATA 4 .comment 0000002b 00000000 00000000 0000005c 2**0 CONTENTS, READONLY 5 .note.GNU-stack 00000000 00000000 00000000 00000087 2**0 CONTENTS, READONLY 6 .eh_frame 00000038 00000000 00000000 00000088 2**2 CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA SYMBOL TABLE: 00000000 l df *ABS* 00000000 hello.c 00000000 l d .text 00000000 .text 00000000 l d .data 00000000 .data
4连接(Linking)
链接:连接器的目的主要是进行重定位和符号解析,gcc连接器是gas提供的,负责将程序的目标文件与所需的所有附加的目标文件连接起来,最终生成可执行文件。附加的目标文件包括静态连接库和动态连接库。
对于上一小节中生成的test.o,将其与C标准输入输出库进行连接,最终生成程序test
gcc hello.o -o hello
可以用readelf命令查看elf文件的详细内容:
Histogram for `.gnu.hash' bucket list length (total of 2 buckets): Length Number % of total Coverage 0 1 ( 50.0%) 1 1 ( 50.0%) 100.0% Version symbols section '.gnu.version' contains 5 entries: Addr: 0000000008048266 Offset: 0x000266 Link: 5 (.dynsym) 000: 0 (*local*) 2 (GLIBC_2.0) 0 (*local*) 2 (GLIBC_2.0) 004: 1 (*global*) Version needs section '.gnu.version_r' contains 1 entries: Addr: 0x0000000008048270 Offset: 0x000270 Link: 6 (.dynstr) 000000: Version: 1 File: libc.so.6 Cnt: 1 0x0010: Name: GLIBC_2.0 Flags: none Version: 2 Notes at offset 0x00000168 with length 0x00000020: Owner Data size Description GNU 0x00000010 NT_GNU_ABI_TAG (ABI version tag) OS: Linux, ABI: 2.6.24
在命令行窗口中,执行./hello, 让它说HelloWorld吧!
作者:trochiluses 发表于2013-9-24 15:01:14 原文链接
阅读:35 评论:0 查看评论