目录
一、GCC编译的工具集
GCC是编译工具,它可以将 C/C++语言编写的程序转换成为处理器能够执行的二进制代码。下表是部分GCC二进制程序的处理工具。
工具 | 作用 |
addr2line | 用来将程序地址转换成其所对应的程序源文件及所对应的代码行,也可以得到所对应的函数。该工具将帮助调试器在调试的过程中定位对应的源代码位置 |
as | 主要用于汇编 |
ld | 主要用于链接 |
ar | 主要用于创建静态库 |
ldd | 可以用于查看一个可执行程序依赖的共享库 |
objcopy | 将一种对象文件翻译成另一种格式,譬如将.bin 转换成.elf、或者将.elf 转换成.bin 等。 |
objdump | 主要的作用是反汇编 |
readelf | 显示有关ELF文件的信息 |
size | 列出可执行文件每个部分的尺寸和总尺寸,代码段、数据段、总大小等 |
二、GCC编译过程
示例程序代码:
#include<stdio.h>
int main(void)
{
printf("Hello World!\n");
return 0;
}
2.1程序编译
输入命令:
gcc test.c -o test
2.2实际编译
GCC的编译分为四个阶段,分别为预处理,编译,汇编和连接
编译阶段 | 编译命令 | 作用 |
---|---|---|
预处理 | gcc -E test.c -o test.i | 编译器将源代码中包含头文件编译进来 |
编译 | gcc -S test.i -o test.s | 检查代码规范性并翻译成汇编语言 |
汇编 | gcc -c test.s -o test.o | 将.s文件转换为目标文件 |
链接 | gcc test.o -o test | 将目标文件转换为可执行文件 |
(1)预处理:
test.i部分代码:
(2) 编译:
执行命令gcc -S test.i -o test.s
,生成test.s文件
test.s部分代码:
(3) 汇编:
执行命令gcc -c test.s -o test.o
,生成test.o文件
(4)连接:
执行命令gcc test.o -o test
,生成test可执行文件
三、ELF文件
1.ELF文件
ELF文件由4部分组成,分别是ELF头(ELF header)、程序头表(Program header table)、节(Section)和节头表(Section header table)。
ELF文件格式如下图,位于ELF Header和Section Header Table 之间的都是段(Section)。一个典型的ELF文件包含下面几个段:
.text:已编译程序的指令代码段。
.rodata:ro 代表 read only,即只读数据(譬如常数 const)。
.data:已初始化的 C 程序全局变量和静态局部变量。
.bss:未初始化的 C 程序全局变量和静态局部变量。
.debug:调试符号表,调试器用此段的信息帮助调试。
- 使用
readelf -S test
查看test可执行文件各个section的信息
There are 34 section headers, starting at offset 0x2158:
节头:
[号] 名称 类型 地址 偏移量
大小 全体大小 旗标 链接 信息 对齐
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 0000000000000238 00000238
000000000000001c 0000000000000000 A 0 0 1
[ 2] .note.ABI-tag NOTE 0000000000000254 00000254
0000000000000020 0000000000000000 A 0 0 4
[ 3] .note.gnu.build-i NOTE 0000000000000274 00000274
0000000000000024 0000000000000000 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0000000000000298 00000298
000000000000001c 0000000000000000 A 5 0 8
……
[31] .symtab SYMTAB 0000000000000000 000017b8
0000000000000660 0000000000000018 32 48 8
[32] .strtab STRTAB 0000000000000000 00001e18
0000000000000202 0000000000000000 0 0 1
[33] .shstrtab STRTAB 0000000000000000 0000201a
000000000000013e 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)
2.反汇编ELF
ELF文件无法被当做普通文本文件打开,如果希望直接查看一个 ELF文件包含的指令和数据,需要使用反汇编的方法,命令为:
objdump -D test
Disassembly of section .init:
00000000000004e8 <_init>:
4e8: 48 83 ec 08 sub $0x8,%rsp
4ec: 48 8b 05 f5 0a 20 00 mov 0x200af5(%rip),%rax # 200fe8 <__gmon_start__>
4f3: 48 85 c0 test %rax,%rax
4f6: 74 02 je 4fa <_init+0x12>
4f8: ff d0 callq *%rax
4fa: 48 83 c4 08 add $0x8,%rsp
4fe: c3 retq……
000000000000063a <main>:
#include<stdio.h>
int main(void)
{
63a: 55 push %rbp
63b: 48 89 e5 mov %rsp,%rbp
printf("Hello World!\n");
63e: 48 8d 3d 9f 00 00 00 lea 0x9f(%rip),%rdi # 6e4 <_IO_stdin_used+0x4>
645: e8 c6 fe ff ff callq 510 <puts@plt>
return 0;
64a: b8 00 00 00 00 mov $0x0,%eax
}
64f: 5d pop %rbp
650: c3 retq
651: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
658: 00 00 00
65b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)0000000000000660 <__libc_csu_init>:
660: 41 57 push %r15
662: 41 56 push %r14
664: 49 89 d7 mov %rdx,%r15
667: 41 55 push %r13
669: 41 54 push %r12
66b: 4c 8d 25 46 07 20 00 lea 0x200746(%rip),%r12 # 200db8 <__frame_dummy_init_array_entry>
672: 55 push %rbp
673: 48 8d 2d 46 07 20 00 lea 0x200746(%rip),%rbp # 200dc0 <__init_array_end>
67a: 53 push %rbx
67b: 41 89 fd mov %edi,%r13d
67e: 49 89 f6 mov %rsi,%r14
681: 4c 29 e5 sub %r12,%rbp
684: 48 83 ec 08 sub $0x8,%rsp
688: 48 c1 fd 03 sar $0x3,%rbp
68c: e8 57 fe ff ff callq 4e8 <_init>
691: 48 85 ed test %rbp,%rbp
694: 74 20 je 6b6 <__libc_csu_init+0x56>
696: 31 db xor %ebx,%ebx
698: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
69f: 00
6a0: 4c 89 fa mov %r15,%rdx
6a3: 4c 89 f6 mov %r14,%rsi
6a6: 44 89 ef mov %r13d,%edi
6a9: 41 ff 14 dc callq *(%r12,%rbx,8)
6ad: 48 83 c3 01 add $0x1,%rbx
6b1: 48 39 dd cmp %rbx,%rbp
6b4: 75 ea jne 6a0 <__libc_csu_init+0x40>
6b6: 48 83 c4 08 add $0x8,%rsp
6ba: 5b pop %rbx
6bb: 5d pop %rbp
6bc: 41 5c pop %r12
6be: 41 5d pop %r13
6c0: 41 5e pop %r14
6c2: 41 5f pop %r15
6c4: c3 retq
6c5: 90 nop
6c6: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
6cd: 00 00 0000000000000006d0 <__libc_csu_fini>:
6d0: f3 c3 repz retqDisassembly of section .fini:
00000000000006d4 <_fini>:
6d4: 48 83 ec 08 sub $0x8,%rsp
6d8: 48 83 c4 08 add $0x8,%rsp
6dc: c3 retq
使用objdump -S
将其反汇编并且将其C语言源代码混合显示出来:
执行命令gcc -o hello -g hello.c
再执行objdump -S test
test: 文件格式 elf64-x86-64
Disassembly of section .init:00000000000004e8 <_init>:
4e8: 48 83 ec 08 sub $0x8,%rsp
4ec: 48 8b 05 f5 0a 20 00 mov 0x200af5(%rip),%rax # 200fe8 <__gmon_start__>
4f3: 48 85 c0 test %rax,%rax
4f6: 74 02 je 4fa <_init+0x12>
4f8: ff d0 callq *%rax
4fa: 48 83 c4 08 add $0x8,%rsp
4fe: c3 retq……
Disassembly of section .text:
0000000000000530 <_start>:
530: 31 ed xor %ebp,%ebp
532: 49 89 d1 mov %rdx,%r9
535: 5e pop %rsi
536: 48 89 e2 mov %rsp,%rdx
539: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
53d: 50 push %rax
53e: 54 push %rsp
53f: 4c 8d 05 8a 01 00 00 lea 0x18a(%rip),%r8 # 6d0 <__libc_csu_fini>
546: 48 8d 0d 13 01 00 00 lea 0x113(%rip),%rcx # 660 <__libc_csu_init>
54d: 48 8d 3d e6 00 00 00 lea 0xe6(%rip),%rdi # 63a <main>
554: ff 15 86 0a 20 00 callq *0x200a86(%rip) # 200fe0 <__libc_start_main@GLIBC_2.2.5>
55a: f4 hlt
55b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)……
00000000000006d0 <__libc_csu_fini>:
6d0: f3 c3 repz retqDisassembly of section .fini:
00000000000006d4 <_fini>:
6d4: 48 83 ec 08 sub $0x8,%rsp
6d8: 48 83 c4 08 add $0x8,%rsp
6dc: c3 retq