gcc编译和目标文件
1 基本概念
Gcc编译4阶段:预处理、编译、汇编、链接
预处理(cpp):处理头文件、宏定义、条件编译等;
编译(ccl):代码检查,生成.s汇编文件;
汇编(as):生成可重定位(全局、外部函数等符号)的.o目标文件;
链接过程(ld):把所有代码和数据组合到单个文件,这个文件可以被加载到内存中执行。
1.1 链接过程类型
编译期链接:针编译可执行文件过程时对静态库、可重定位目标文件的链接
加载期链接:针对动态库的运行时链接,在被加载器加载到内存执行时。
运行时链接:针对动态库,由应用程序来动态加载库文件,相关函数如:dladdr, dlclose, dlerror, dlopen, dlsym, dlvsym。Java本地接口即通过运行时链接方式动态链接和加载使用C/C++编译生成的动态链接库。
1.2 目标文件形式
可重定位目标文件:编译、汇编后的.o目标文件,可重定位;
可执行目标文件:二进制可执行目标文件,静态链接后的bin文件
共享目标文件:动态链接库
2 Gcc编译过程示例
2.1 示例代码
三个文件
add.h文件内容:
int add(int a,int b);
add.c文件内容:
int add(int a,int b)
{
return a+b;
}
main.c文件内容:
#include “add.h”
int a = 1;
int b = 2;
int main()
{
int res=add(a,b);
}
2.2 预处理
2.2.1 main.i
gcc -E main.c -o main.i
生成对main.c预处理后的main.i文件
# 1 "main.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "main.c"
# 1 "add.h" 1
int add(int a,int b);
# 2 "main.c" 2
int a = 1;
int b = 2;
int main()
{
int res=add(a,b);
}
2.2.2 add.i
# 1 "add.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "add.c"
int add(int a,int b)
{
return a+b;
}
2.3 编译
2.3.1 main.s
cc1 -m32 main.i -o main.s
生成汇编文件main.s
备注:
1、 这里以32bit进行编译
2、 若提示命令找不到,原因是cc1所在目录不在$PATH环境变量中,可使用可使用绝对路径,如/usr/lib/gcc/x86_64-linux-gnu/4.8.4/cc1 -m32 main.i -o main.s
3、 cc1第3个字符是数字1,不是字母l
.file "main.i"
.globl a
.data
.align 4
.type a, @object
.size a, 4
a:
.long 1
.globl b
.align 4
.type b, @object
.size b, 4
b:
.long 2
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl b(%rip), %edx
movl a(%rip), %eax
movl %edx, %esi
movl %eax, %edi
call add
movl %eax, -4(%rbp)
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.4) 4.8.4"
.section .note.GNU-stack,"",@progbits
2.3.2 add.s
.file "add.i"
.text
.globl add
.type add, @function
add:
.LFB0:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
movl 12(%ebp), %eax
movl 8(%ebp), %edx
addl %edx, %eax
popl %ebp
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE0:
.size add, .-add
.ident "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.4) 4.8.4"
.section .note.GNU-stack,"",@progbits
2.4 汇编和反汇编
as --32 -o main.o main.s
生成可重定位目标文件main.o
2.4.1 main.o
此文件不好使用cat直接查看,会显示一堆乱码。
可使用objdump进行反汇编命令选项查看:
objdump -dx main.o
-d:反汇编
-x:显示所有header
命令结果显示如下:
main.o: file format elf32-i386
main.o
architecture: i386, flags 0x00000011:
HAS_RELOC, HAS_SYMS
start address 0x00000000
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000026 00000000 00000000 00000034 2**0
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
1 .data 00000008 00000000 00000000 0000005c 2**2
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000000 00000000 00000000 00000064 2**0
ALLOC
3 .comment 0000002c 00000000 00000000 00000064 2**0
CONTENTS, READONLY
4 .note.GNU-stack 00000000 00000000 00000000 00000090 2**0
CONTENTS, READONLY
5 .eh_frame 00000038 00000000 00000000 00000090 2**2
CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
SYMBOL TABLE:
00000000 l df *ABS* 00000000 main.i
00000000 l d .text 00000000 .text
00000000 l d .data 00000000 .data
00000000 l d .bss 00000000 .bss
00000000 l d .note.GNU-stack 00000000 .note.GNU-stack
00000000 l d .eh_frame 00000000 .eh_frame
00000000 l d .comment 00000000 .comment
00000000 g O .data 00000004 a
00000004 g O .data 00000004 b
00000000 g F .text 00000026 main
00000000 *UND* 00000000 add
Disassembly of section .text:
00000000 <main>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 e4 f0 and $0xfffffff0,%esp
6: 83 ec 20 sub $0x20,%esp
9: 8b 15 00 00 00 00 mov 0x0,%edx
b: R_386_32 b
f: a1 00 00 00 00 mov 0x0,%eax
10: R_386_32 a
14: 89 54 24 04 mov %edx,0x4(%esp)
18: 89 04 24 mov %eax,(%esp)
1b: e8 fc ff ff ff call 1c <main+0x1c>
1c: R_386_PC32 add
20: 89 44 24 1c mov %eax,0x1c(%esp)
24: c9 leave
25: c3 ret
2.4.2 add.o
main.o: file format elf32-i386
main.o
architecture: i386, flags 0x00000011:
HAS_RELOC, HAS_SYMS
start address 0x00000000
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000026 00000000 00000000 00000034 2**0
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
1 .data 00000008 00000000 00000000 0000005c 2**2
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000000 00000000 00000000 00000064 2**0
ALLOC
3 .comment 0000002c 00000000 00000000 00000064 2**0
CONTENTS, READONLY
4 .note.GNU-stack 00000000 00000000 00000000 00000090 2**0
CONTENTS, READONLY
5 .eh_frame 00000038 00000000 00000000 00000090 2**2
CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
SYMBOL TABLE:
00000000 l df *ABS* 00000000 main.i
00000000 l d .text 00000000 .text
00000000 l d .data 00000000 .data
00000000 l d .bss 00000000 .bss
00000000 l d .note.GNU-stack 00000000 .note.GNU-stack
00000000 l d .eh_frame 00000000 .eh_frame
00000000 l d .comment 00000000 .comment
00000000 g O .data 00000004 a
00000004 g O .data 00000004 b
00000000 g F .text 00000026 main
00000000 *UND* 00000000 add
Disassembly of section .text:
00000000 <main>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 e4 f0 and $0xfffffff0,%esp
6: 83 ec 20 sub $0x20,%esp
9: 8b 15 00 00 00 00 mov 0x0,%edx
b: R_386_32 b
f: a1 00 00 00 00 mov 0x0,%eax
10: R_386_32 a
14: 89 54 24 04 mov %edx,0x4(%esp)
18: 89 04 24 mov %eax,(%esp)
1b: e8 fc ff ff ff call 1c <main+0x1c>
1c: R_386_PC32 add
20: 89 44 24 1c mov %eax,0x1c(%esp)
24: c9 leave
25: c3 ret
2.5 链接
ld -m elf_i386 -o main main.o add.o
2.6 相关命令汇总
cpp main.c main.i
cc1 main.i -o main.s
as --32 -o main.o main.s
objdump -dx main.o >main.o.txt
cpp add.c add.i
/usr/lib/gcc/x86_64-linux-gnu/4.8.4/cc1 -m32 add.i -o add.s
as --32 -o add.o add.s
objdump -dx add.o >add.o.txt
链接:
ld -m elf_i386 -o main main.o add.o
备注:cc1命令使用的版本路径
/usr/lib/gcc/x86_64-linux-gnu/4.8.4/ cc1
3 可执行文件分析
3.1 可执行ELF文件空间布局
3.2 可执行文件反编译
objdump -dx main
3.2.1 文件基本信息
main: file format elf32-i386
main
architecture: i386, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x08048094
3.2.2 程序头部表信息
Program Header:
LOAD off 0x00000000 vaddr 0x08048000 paddr 0x08048000 align 212
filesz 0x00000120 memsz 0x00000120 flags r-x
LOAD off 0x00000120 vaddr 0x08049120 paddr 0x08049120 align 212
filesz 0x00000008 memsz 0x00000008 flags rw-
STACK off 0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**4
filesz 0x00000000 memsz 0x00000000 flags rw-
说明:
Off:目标文件中的偏移
Vadd/paddr:内存地址
Align:对齐要求
Filesz:目标文件中的段大小
Memsz:内存中的段大小
Flags:运行时访问权限
3.2.3 段信息
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000033 08048094 08048094 00000094 20
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .eh_frame 00000058 080480c8 080480c8 000000c8 22
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .data 00000008 08049120 08049120 00000120 22
CONTENTS, ALLOC, LOAD, DATA
3 .comment 0000002b 00000000 00000000 00000128 20
CONTENTS, READONLY
3.2.4 符号表信息
SYMBOL TABLE:
08048094 l d .text 00000000 .text
080480c8 l d .eh_frame 00000000 .eh_frame
08049120 l d .data 00000000 .data
00000000 l d .comment 00000000 .comment
00000000 l df ABS 00000000 main.i
00000000 l df ABS 00000000 add.i
00000000 l df ABS 00000000
08049124 g O .data 00000004 b
080480ba g F .text 0000000d add
00000000 UND 00000000 _start
08049128 g .data 00000000 __bss_start
08048094 g F .text 00000026 main
08049128 g .data 00000000 _edata
08049128 g .data 00000000 _end
08049120 g O .data 00000004 a
3.2.5 反汇编信息
Disassembly of section .text:
08048094 :
8048094: 55 push %ebp
8048095: 89 e5 mov %esp,%ebp
8048097: 83 e4 f0 and $0xfffffff0,%esp
804809a: 83 ec 20 sub $0x20,%esp
804809d: 8b 15 24 91 04 08 mov 0x8049124,%edx
80480a3: a1 20 91 04 08 mov 0x8049120,%eax
80480a8: 89 54 24 04 mov %edx,0x4(%esp)
80480ac: 89 04 24 mov %eax,(%esp)
80480af: e8 06 00 00 00 call 80480ba
80480b4: 89 44 24 1c mov %eax,0x1c(%esp)
80480b8: c9 leave
80480b9: c3 ret
080480ba :
80480ba: 55 push %ebp
80480bb: 89 e5 mov %esp,%ebp
80480bd: 8b 45 0c mov 0xc(%ebp),%eax
80480c0: 8b 55 08 mov 0x8(%ebp),%edx
80480c3: 01 d0 add %edx,%eax
80480c5: 5d pop %ebp
80480c6: c3 ret
4 加载可执行目标文件
4.1 运行时内存映像
5 参考资料
1、《深入理解计算机系统》,【美】Randal E.Bryant,David R.O’Hallaron
, 第3版,2019年3月,机械工业出版社,