GCC工作流程
1 什么是GCC
2 GCC工作流程
预处理:宏头文件展开、宏替换等
gcc test.c -E -o test.i
编译:
gcc test.i -S -o test.s
汇编:
gcc test.i -c -o test.o
连接:
gcc test.o -o test.out
也可以直接:
gcc test.c
3 流程举例
3.1 预处理:宏头文件展开、宏替换等
#include <stdio.h>
void main(){
return;
}
gcc test.c -E -o test.i
结果展示(方便展示略去一些):
1 "main.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 368 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "main.c" 2
# 1 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/stdio.h" 1 3 4
# 64 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/stdio.h" 3 4
# 1 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/_stdio.h" 1 3 4
# 68 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/_stdio.h" 3 4
# 1 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/cdefs.h" 1 3 4
typedef signed char __int8_t;
typedef unsigned char __uint8_t;
typedef short __int16_t;
typedef unsigned short __uint16_t;
typedef int __int32_t;
typedef unsigned int __uint32_t;
typedef long long __int64_t;
typedef union {
char __mbstate8[128];
long long _mbstateL;
} __mbstate_t;
typedef __mbstate_t __darwin_mbstate_t;
typedef long int __darwin_ptrdiff_t;
FILE *fopen(const char * restrict __filename, const char * restrict __mode) __asm("_" "fopen" );
int fprintf(FILE * restrict, const char * restrict, ...) __attribute__((__format__ (__printf__, 2, 3)));
int fputc(int, FILE *);
int fputs(const char * restrict, FILE * restrict) __asm("_" "fputs" );
size_t fread(void * restrict __ptr, size_t __size, size_t __nitems, FILE * restrict __stream);
FILE *freopen(const char * restrict, const char * restrict,
FILE * restrict) __asm("_" "freopen" );
int fscanf(FILE * restrict, const char * restrict, ...) __attribute__((__format__ (__scanf__, 2, 3)));
int fseek(FILE *, long, int);
int fsetpos(FILE *, const fpos_t *);
long ftell(FILE *);
size_t fwrite(const void * restrict __ptr, size_t __size, size_t __nitems, FILE * restrict __stream) __asm("_" "fwrite" );
int getc(FILE *);
int getchar(void);
char *gets(char *);
extern int __vsnprintf_chk (char * restrict, size_t, int, size_t,
const char * restrict, va_list);
# 408 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/stdio.h" 2 3 4
# 2 "main.c" 2
void main(){
return;
}
3.2 编译
得到汇编语言文件
gcc test.i -S -o test.s
linux编译
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 9.4.0-1ubuntu1~20.04.2) 9.4.0"
.section .note.GNU-stack,"",@progbits
.section .note.gnu.property,"a"
.align 8
.long 1f - 0f
.long 4f - 1f
.long 5
0:
.string "GNU"
1:
.align 8
.long 0xc0000002
.long 3f - 2f
2:
.long 0x3
3:
.align 8
4:
mac环境编译
.section __TEXT,__text,regular,pure_instructions
.build_version macos, 11, 0 sdk_version 11, 3
.globl _main ## -- Begin function main
.p2align 4, 0x90
_main: ## @main
.cfi_startproc
## %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
popq %rbp
retq
.cfi_endproc
## -- End function
.subsections_via_symbols
3.3 汇编
得到目标代码文件
gcc test.i -c -o test.o
目标文件(object file)是编译器将源代码编译成机器码后生成的中间文件,通常具有 .o 或 .obj 扩展名。目标文件包含了程序的一部分代码和数据,但还没有链接成最终的可执行文件。目标文件的内容通常包括以下几部分:
- 机器码:编译后的二进制指令,可以直接在处理器上执行。
- 数据段:
- .data 段:包含已初始化的全局变量和静态变量。
- .bss 段:包含未初始化的全局变量和静态变量。
- .rodata 段:包含只读数据,例如字符串常量。
- 符号表:列出了所有在源代码中定义或引用的符号(如函数名、变量名),以及它们在目标文件中的位置。
- 重定位信息:当目标文件被链接成可执行文件时,用于调整代码和数据的地址。这些信息告诉链接器如何修正程序中对其他模块或库中的符号的引用。
- 调试信息(如果编译时启用了调试选项):包含源代码行号、变量名等信息,用于调试工具。
- 段表:描述各个段(如代码段、数据段)的大小、位置和属性。
3.3.1 显示汇编代码
# 此命令查看obj文件
objdump -d test.o
linux环境
test.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <main>:
0: f3 0f 1e fa endbr64
4: 55 push %rbp
5: 48 89 e5 mov %rsp,%rbp
8: 90 nop
9: 5d pop %rbp
a: c3 retq
mac环境
test.o: file format mach-o 64-bit x86-64
Disassembly of section __TEXT,__text:
0000000000000000 <_main>:
0: 55 pushq %rbp
1: 48 89 e5 movq %rsp, %rbp
4: 5d popq %rbp
5: c3 retq
可以看到不同环境文件格式不一样如elf
3.3.2 显示符号表
objdump -t test.o
test.o: file format mach-o 64-bit x86-64
SYMBOL TABLE:
0000000000000000 g F __TEXT,__text _main
3.3.3 显示所有段的信息
objdump -h test.o
test.o: file format mach-o 64-bit x86-64
Sections:
Idx Name Size VMA Type
0 __text 00000006 0000000000000000 TEXT
1 __compact_unwind 00000020 0000000000000008 DATA
2 __eh_frame 00000040 0000000000000028 DATA
3.3.4 显示重定位信息
objdump -r test.o
test.o: file format mach-o 64-bit x86-64
RELOCATION RECORDS FOR [__compact_unwind]:
OFFSET TYPE VALUE
0000000000000000 X86_64_RELOC_UNSIGNED __text
3.4 连接
gcc test.o -o test.out
也可以直接:
gcc test.c
linux环境运行objdump -x a.out
a.out: file format elf64-x86-64
a.out
architecture: i386:x86-64, flags 0x00000150:
HAS_SYMS, DYNAMIC, D_PAGED
start address 0x0000000000001040
Program Header:
PHDR off 0x0000000000000040 vaddr 0x0000000000000040 paddr 0x0000000000000040 align 2**3
filesz 0x00000000000002d8 memsz 0x00000000000002d8 flags r--
INTERP off 0x0000000000000318 vaddr 0x0000000000000318 paddr 0x0000000000000318 align 2**0
filesz 0x000000000000001c memsz 0x000000000000001c flags r--
LOAD off 0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**12
filesz 0x00000000000005c8 memsz 0x00000000000005c8 flags r--
LOAD off 0x0000000000001000 vaddr 0x0000000000001000 paddr 0x0000000000001000 align 2**12
filesz 0x00000000000001c5 memsz 0x00000000000001c5 flags r-x
LOAD off 0x0000000000002000 vaddr 0x0000000000002000 paddr 0x0000000000002000 align 2**12
filesz 0x0000000000000130 memsz 0x0000000000000130 flags r--
LOAD off 0x0000000000002df0 vaddr 0x0000000000003df0 paddr 0x0000000000003df0 align 2**12
filesz 0x0000000000000220 memsz 0x0000000000000228 flags rw-
DYNAMIC off 0x0000000000002e00 vaddr 0x0000000000003e00 paddr 0x0000000000003e00 align 2**3
filesz 0x00000000000001c0 memsz 0x00000000000001c0 flags rw-
NOTE off 0x0000000000000338 vaddr 0x0000000000000338 paddr 0x0000000000000338 align 2**3
filesz 0x0000000000000020 memsz 0x0000000000000020 flags r--
NOTE off 0x0000000000000358 vaddr 0x0000000000000358 paddr 0x0000000000000358 align 2**2
filesz 0x0000000000000044 memsz 0x0000000000000044 flags r--
0x6474e553 off 0x0000000000000338 vaddr 0x0000000000000338 paddr 0x0000000000000338 align 2**3
filesz 0x0000000000000020 memsz 0x0000000000000020 flags r--
EH_FRAME off 0x0000000000002004 vaddr 0x0000000000002004 paddr 0x0000000000002004 align 2**2
filesz 0x000000000000003c memsz 0x000000000000003c flags r--
STACK off 0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**4
filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-
RELRO off 0x0000000000002df0 vaddr 0x0000000000003df0 paddr 0x0000000000003df0 align 2**0
filesz 0x0000000000000210 memsz 0x0000000000000210 flags r--
Dynamic Section:
NEEDED libc.so.6
INIT 0x0000000000001000
FINI 0x00000000000011b8
INIT_ARRAY 0x0000000000003df0
INIT_ARRAYSZ 0x0000000000000008
FINI_ARRAY 0x0000000000003df8
FINI_ARRAYSZ 0x0000000000000008
GNU_HASH 0x00000000000003a0
STRTAB 0x0000000000000458
SYMTAB 0x00000000000003c8
STRSZ 0x000000000000007d
SYMENT 0x0000000000000018
DEBUG 0x0000000000000000
PLTGOT 0x0000000000003fc0
RELA 0x0000000000000508
RELASZ 0x00000000000000c0
RELAENT 0x0000000000000018
FLAGS 0x0000000000000008
FLAGS_1 0x0000000008000001
VERNEED 0x00000000000004e8
VERNEEDNUM 0x0000000000000001
VERSYM 0x00000000000004d6
RELACOUNT 0x0000000000000003
Version References:
required from libc.so.6:
0x09691a75 0x00 02 GLIBC_2.2.5
Sections:
Idx Name Size VMA LMA File off Algn
0 .interp 0000001c 0000000000000318 0000000000000318 00000318 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .note.gnu.property 00000020 0000000000000338 0000000000000338 00000338 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .note.gnu.build-id 00000024 0000000000000358 0000000000000358 00000358 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .note.ABI-tag 00000020 000000000000037c 000000000000037c 0000037c 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .gnu.hash 00000024 00000000000003a0 00000000000003a0 000003a0 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
5 .dynsym 00000090 00000000000003c8 00000000000003c8 000003c8 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
6 .dynstr 0000007d 0000000000000458 0000000000000458 00000458 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
7 .gnu.version 0000000c 00000000000004d6 00000000000004d6 000004d6 2**1
CONTENTS, ALLOC, LOAD, READONLY, DATA
8 .gnu.version_r 00000020 00000000000004e8 00000000000004e8 000004e8 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
9 .rela.dyn 000000c0 0000000000000508 0000000000000508 00000508 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
10 .init 0000001b 0000000000001000 0000000000001000 00001000 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
11 .plt 00000010 0000000000001020 0000000000001020 00001020 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
12 .plt.got 00000010 0000000000001030 0000000000001030 00001030 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
13 .text 00000175 0000000000001040 0000000000001040 00001040 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
14 .fini 0000000d 00000000000011b8 00000000000011b8 000011b8 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
15 .rodata 00000004 0000000000002000 0000000000002000 00002000 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
16 .eh_frame_hdr 0000003c 0000000000002004 0000000000002004 00002004 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
17 .eh_frame 000000f0 0000000000002040 0000000000002040 00002040 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
18 .init_array 00000008 0000000000003df0 0000000000003df0 00002df0 2**3
CONTENTS, ALLOC, LOAD, DATA
19 .fini_array 00000008 0000000000003df8 0000000000003df8 00002df8 2**3
CONTENTS, ALLOC, LOAD, DATA
20 .dynamic 000001c0 0000000000003e00 0000000000003e00 00002e00 2**3
CONTENTS, ALLOC, LOAD, DATA
21 .got 00000040 0000000000003fc0 0000000000003fc0 00002fc0 2**3
CONTENTS, ALLOC, LOAD, DATA
22 .data 00000010 0000000000004000 0000000000004000 00003000 2**3
CONTENTS, ALLOC, LOAD, DATA
23 .bss 00000008 0000000000004010 0000000000004010 00003010 2**0
ALLOC
24 .comment 0000002b 0000000000000000 0000000000000000 00003010 2**0
CONTENTS, READONLY
SYMBOL TABLE:
0000000000000318 l d .interp 0000000000000000 .interp
0000000000000338 l d .note.gnu.property 0000000000000000 .note.gnu.property
0000000000000358 l d .note.gnu.build-id 0000000000000000 .note.gnu.build-id
000000000000037c l d .note.ABI-tag 0000000000000000 .note.ABI-tag
00000000000003a0 l d .gnu.hash 0000000000000000 .gnu.hash
00000000000003c8 l d .dynsym 0000000000000000 .dynsym
0000000000000458 l d .dynstr 0000000000000000 .dynstr
00000000000004d6 l d .gnu.version 0000000000000000 .gnu.version
00000000000004e8 l d .gnu.version_r 0000000000000000 .gnu.version_r
0000000000000508 l d .rela.dyn 0000000000000000 .rela.dyn
0000000000001000 l d .init 0000000000000000 .init
0000000000001020 l d .plt 0000000000000000 .plt
0000000000001030 l d .plt.got 0000000000000000 .plt.got
0000000000001040 l d .text 0000000000000000 .text
00000000000011b8 l d .fini 0000000000000000 .fini
0000000000002000 l d .rodata 0000000000000000 .rodata
0000000000002004 l d .eh_frame_hdr 0000000000000000 .eh_frame_hdr
0000000000002040 l d .eh_frame 0000000000000000 .eh_frame
0000000000003df0 l d .init_array 0000000000000000 .init_array
0000000000003df8 l d .fini_array 0000000000000000 .fini_array
0000000000003e00 l d .dynamic 0000000000000000 .dynamic
0000000000003fc0 l d .got 0000000000000000 .got
0000000000004000 l d .data 0000000000000000 .data
0000000000004010 l d .bss 0000000000000000 .bss
0000000000000000 l d .comment 0000000000000000 .comment
0000000000000000 l df *ABS* 0000000000000000 crtstuff.c
0000000000001070 l F .text 0000000000000000 deregister_tm_clones
00000000000010a0 l F .text 0000000000000000 register_tm_clones
00000000000010e0 l F .text 0000000000000000 __do_global_dtors_aux
0000000000004010 l O .bss 0000000000000001 completed.8061
0000000000003df8 l O .fini_array 0000000000000000 __do_global_dtors_aux_fini_array_entry
0000000000001120 l F .text 0000000000000000 frame_dummy
0000000000003df0 l O .init_array 0000000000000000 __frame_dummy_init_array_entry
0000000000000000 l df *ABS* 0000000000000000 test.c
0000000000000000 l df *ABS* 0000000000000000 crtstuff.c
000000000000212c l O .eh_frame 0000000000000000 __FRAME_END__
0000000000000000 l df *ABS* 0000000000000000
0000000000003df8 l .init_array 0000000000000000 __init_array_end
0000000000003e00 l O .dynamic 0000000000000000 _DYNAMIC
0000000000003df0 l .init_array 0000000000000000 __init_array_start
0000000000002004 l .eh_frame_hdr 0000000000000000 __GNU_EH_FRAME_HDR
0000000000003fc0 l O .got 0000000000000000 _GLOBAL_OFFSET_TABLE_
0000000000001000 l F .init 0000000000000000 _init
00000000000011b0 g F .text 0000000000000005 __libc_csu_fini
0000000000000000 w *UND* 0000000000000000 _ITM_deregisterTMCloneTable
0000000000004000 w .data 0000000000000000 data_start
0000000000004010 g .data 0000000000000000 _edata
00000000000011b8 g F .fini 0000000000000000 .hidden _fini
0000000000000000 F *UND* 0000000000000000 __libc_start_main@@GLIBC_2.2.5
0000000000004000 g .data 0000000000000000 __data_start
0000000000000000 w *UND* 0000000000000000 __gmon_start__
0000000000004008 g O .data 0000000000000000 .hidden __dso_handle
0000000000002000 g O .rodata 0000000000000004 _IO_stdin_used
0000000000001140 g F .text 0000000000000065 __libc_csu_init
0000000000004018 g .bss 0000000000000000 _end
0000000000001040 g F .text 000000000000002f _start
0000000000004010 g .bss 0000000000000000 __bss_start
0000000000001129 g F .text 000000000000000b main
0000000000004010 g O .data 0000000000000000 .hidden __TMC_END__
0000000000000000 w *UND* 0000000000000000 _ITM_registerTMCloneTable
0000000000000000 w F *UND* 0000000000000000 __cxa_finalize@@GLIBC_2.2.5
objdump -d a.out
a.out: file format elf64-x86-64
Disassembly of section .init:
0000000000001000 <_init>:
1000: f3 0f 1e fa endbr64
1004: 48 83 ec 08 sub $0x8,%rsp
1008: 48 8b 05 d9 2f 00 00 mov 0x2fd9(%rip),%rax # 3fe8 <__gmon_start__>
100f: 48 85 c0 test %rax,%rax
1012: 74 02 je 1016 <_init+0x16>
1014: ff d0 callq *%rax
1016: 48 83 c4 08 add $0x8,%rsp
101a: c3 retq
Disassembly of section .plt:
0000000000001020 <.plt>:
1020: ff 35 a2 2f 00 00 pushq 0x2fa2(%rip) # 3fc8 <_GLOBAL_OFFSET_TABLE_+0x8>
1026: f2 ff 25 a3 2f 00 00 bnd jmpq *0x2fa3(%rip) # 3fd0 <_GLOBAL_OFFSET_TABLE_+0x10>
102d: 0f 1f 00 nopl (%rax)
Disassembly of section .plt.got:
0000000000001030 <__cxa_finalize@plt>:
1030: f3 0f 1e fa endbr64
1034: f2 ff 25 bd 2f 00 00 bnd jmpq *0x2fbd(%rip) # 3ff8 <__cxa_finalize@GLIBC_2.2.5>
103b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
Disassembly of section .text:
0000000000001040 <_start>:
1040: f3 0f 1e fa endbr64
1044: 31 ed xor %ebp,%ebp
1046: 49 89 d1 mov %rdx,%r9
1049: 5e pop %rsi
104a: 48 89 e2 mov %rsp,%rdx
104d: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
1051: 50 push %rax
1052: 54 push %rsp
1053: 4c 8d 05 56 01 00 00 lea 0x156(%rip),%r8 # 11b0 <__libc_csu_fini>
105a: 48 8d 0d df 00 00 00 lea 0xdf(%rip),%rcx # 1140 <__libc_csu_init>
1061: 48 8d 3d c1 00 00 00 lea 0xc1(%rip),%rdi # 1129 <main>
1068: ff 15 72 2f 00 00 callq *0x2f72(%rip) # 3fe0 <__libc_start_main@GLIBC_2.2.5>
106e: f4 hlt
106f: 90 nop
0000000000001070 <deregister_tm_clones>:
1070: 48 8d 3d 99 2f 00 00 lea 0x2f99(%rip),%rdi # 4010 <__TMC_END__>
1077: 48 8d 05 92 2f 00 00 lea 0x2f92(%rip),%rax # 4010 <__TMC_END__>
107e: 48 39 f8 cmp %rdi,%rax
1081: 74 15 je 1098 <deregister_tm_clones+0x28>
1083: 48 8b 05 4e 2f 00 00 mov 0x2f4e(%rip),%rax # 3fd8 <_ITM_deregisterTMCloneTable>
108a: 48 85 c0 test %rax,%rax
108d: 74 09 je 1098 <deregister_tm_clones+0x28>
108f: ff e0 jmpq *%rax
1091: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
1098: c3 retq
1099: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
00000000000010a0 <register_tm_clones>:
10a0: 48 8d 3d 69 2f 00 00 lea 0x2f69(%rip),%rdi # 4010 <__TMC_END__>
10a7: 48 8d 35 62 2f 00 00 lea 0x2f62(%rip),%rsi # 4010 <__TMC_END__>
10ae: 48 29 fe sub %rdi,%rsi
10b1: 48 89 f0 mov %rsi,%rax
10b4: 48 c1 ee 3f shr $0x3f,%rsi
10b8: 48 c1 f8 03 sar $0x3,%rax
10bc: 48 01 c6 add %rax,%rsi
10bf: 48 d1 fe sar %rsi
10c2: 74 14 je 10d8 <register_tm_clones+0x38>
10c4: 48 8b 05 25 2f 00 00 mov 0x2f25(%rip),%rax # 3ff0 <_ITM_registerTMCloneTable>
10cb: 48 85 c0 test %rax,%rax
10ce: 74 08 je 10d8 <register_tm_clones+0x38>
10d0: ff e0 jmpq *%rax
10d2: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
10d8: c3 retq
10d9: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
00000000000010e0 <__do_global_dtors_aux>:
10e0: f3 0f 1e fa endbr64
10e4: 80 3d 25 2f 00 00 00 cmpb $0x0,0x2f25(%rip) # 4010 <__TMC_END__>
10eb: 75 2b jne 1118 <__do_global_dtors_aux+0x38>
10ed: 55 push %rbp
10ee: 48 83 3d 02 2f 00 00 cmpq $0x0,0x2f02(%rip) # 3ff8 <__cxa_finalize@GLIBC_2.2.5>
10f5: 00
10f6: 48 89 e5 mov %rsp,%rbp
10f9: 74 0c je 1107 <__do_global_dtors_aux+0x27>
10fb: 48 8b 3d 06 2f 00 00 mov 0x2f06(%rip),%rdi # 4008 <__dso_handle>
1102: e8 29 ff ff ff callq 1030 <__cxa_finalize@plt>
1107: e8 64 ff ff ff callq 1070 <deregister_tm_clones>
110c: c6 05 fd 2e 00 00 01 movb $0x1,0x2efd(%rip) # 4010 <__TMC_END__>
1113: 5d pop %rbp
1114: c3 retq
1115: 0f 1f 00 nopl (%rax)
1118: c3 retq
1119: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
0000000000001120 <frame_dummy>:
1120: f3 0f 1e fa endbr64
1124: e9 77 ff ff ff jmpq 10a0 <register_tm_clones>
0000000000001129 <main>:
1129: f3 0f 1e fa endbr64
112d: 55 push %rbp
112e: 48 89 e5 mov %rsp,%rbp
1131: 90 nop
1132: 5d pop %rbp
1133: c3 retq
1134: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
113b: 00 00 00
113e: 66 90 xchg %ax,%ax
0000000000001140 <__libc_csu_init>:
1140: f3 0f 1e fa endbr64
1144: 41 57 push %r15
1146: 4c 8d 3d a3 2c 00 00 lea 0x2ca3(%rip),%r15 # 3df0 <__frame_dummy_init_array_entry>
114d: 41 56 push %r14
114f: 49 89 d6 mov %rdx,%r14
1152: 41 55 push %r13
1154: 49 89 f5 mov %rsi,%r13
1157: 41 54 push %r12
1159: 41 89 fc mov %edi,%r12d
115c: 55 push %rbp
115d: 48 8d 2d 94 2c 00 00 lea 0x2c94(%rip),%rbp # 3df8 <__do_global_dtors_aux_fini_array_entry>
1164: 53 push %rbx
1165: 4c 29 fd sub %r15,%rbp
1168: 48 83 ec 08 sub $0x8,%rsp
116c: e8 8f fe ff ff callq 1000 <_init>
1171: 48 c1 fd 03 sar $0x3,%rbp
1175: 74 1f je 1196 <__libc_csu_init+0x56>
1177: 31 db xor %ebx,%ebx
1179: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
1180: 4c 89 f2 mov %r14,%rdx
1183: 4c 89 ee mov %r13,%rsi
1186: 44 89 e7 mov %r12d,%edi
1189: 41 ff 14 df callq *(%r15,%rbx,8)
118d: 48 83 c3 01 add $0x1,%rbx
1191: 48 39 dd cmp %rbx,%rbp
1194: 75 ea jne 1180 <__libc_csu_init+0x40>
1196: 48 83 c4 08 add $0x8,%rsp
119a: 5b pop %rbx
119b: 5d pop %rbp
119c: 41 5c pop %r12
119e: 41 5d pop %r13
11a0: 41 5e pop %r14
11a2: 41 5f pop %r15
11a4: c3 retq
11a5: 66 66 2e 0f 1f 84 00 data16 nopw %cs:0x0(%rax,%rax,1)
11ac: 00 00 00 00
00000000000011b0 <__libc_csu_fini>:
11b0: f3 0f 1e fa endbr64
11b4: c3 retq
Disassembly of section .fini:
00000000000011b8 <_fini>:
11b8: f3 0f 1e fa endbr64
11bc: 48 83 ec 08 sub $0x8,%rsp
11c0: 48 83 c4 08 add $0x8,%rsp
11c4: c3 retq
4 gcc 与 g++ 误区
5 装载运行
实际上,“C 语言代码 - 汇编代码 - 机器码” 这个过程,在我们的计算机上进行的时候是由两部分组成的。
第一个部分由编译(Compile)、汇编(Assemble)以及链接(Link)三个阶段组成。在这三个阶段完成之后,我们就生成了一个可执行文件。
第二部分,我们通过装载器(Loader)把可执行文件装载(Load)到内存中。CPU 从内存中读取指令和数据,来开始真正执行程序。
在 Linux 下,可执行文件和目标文件所使用的都是一种叫 ELF——Execuatable and Linkable File Format的文件格式,中文名字叫可执行与可链接文件格式,这里面不仅存放了编译成的汇编指令,还保留了很多别的数据。
比如我们过去所有 objdump 出来的代码里,你都可以看到对应的函数名称,像 add、main 等等,乃至你自己定义的全局可以访问的变量名称,都存放在这个 ELF 格式文件里。
这些名字和它们对应的地址,在 ELF 文件里面,存储在一个叫作符号表(Symbols Table)的位置里。
符号表相当于一个地址簿,把名字和地址关联了起来。我们先只关注和我们的 add 以及 main 函数相关的部分。你会发现,这里面,main 函数里调用 add 的跳转地址,不再是下一条指令的地址了,而是 add 函数的入口地址了,这就是 EFL 格式和链接器的功劳。
link_example: file format elf64-x86-64
Disassembly of section .init:
...
Disassembly of section .plt:
...
Disassembly of section .plt.got:
...
Disassembly of section .text:
...
6b0: 55 push rbp
6b1: 48 89 e5 mov rbp,rsp
6b4: 89 7d fc mov DWORD PTR [rbp-0x4],edi
6b7: 89 75 f8 mov DWORD PTR [rbp-0x8],esi
6ba: 8b 55 fc mov edx,DWORD PTR [rbp-0x4]
6bd: 8b 45 f8 mov eax,DWORD PTR [rbp-0x8]
6c0: 01 d0 add eax,edx
6c2: 5d pop rbp
6c3: c3 ret
00000000000006c4 <main>:
6c4: 55 push rbp
6c5: 48 89 e5 mov rbp,rsp
6c8: 48 83 ec 10 sub rsp,0x10
6cc: c7 45 fc 0a 00 00 00 mov DWORD PTR [rbp-0x4],0xa
6d3: c7 45 f8 05 00 00 00 mov DWORD PTR [rbp-0x8],0x5
6da: 8b 55 f8 mov edx,DWORD PTR [rbp-0x8]
6dd: 8b 45 fc mov eax,DWORD PTR [rbp-0x4]
6e0: 89 d6 mov esi,edx
6e2: 89 c7 mov edi,eax
6e4: b8 00 00 00 00 mov eax,0x0
6e9: e8 c2 ff ff ff call 6b0 <add>
6ee: 89 45 f4 mov DWORD PTR [rbp-0xc],eax
6f1: 8b 45 f4 mov eax,DWORD PTR [rbp-0xc]
6f4: 89 c6 mov esi,eax
6f6: 48 8d 3d 97 00 00 00 lea rdi,[rip+0x97] # 794 <_IO_stdin_used+0x4>
6fd: b8 00 00 00 00 mov eax,0x0
702: e8 59 fe ff ff call 560 <printf@plt>
707: b8 00 00 00 00 mov eax,0x0
70c: c9 leave
70d: c3 ret
70e: 66 90 xchg ax,ax
...
Disassembly of section .fini:
...
ELF 文件格式把各种信息,分成一个一个的 Section 保存起来。
ELF 有一个基本的文件头(File Header),用来表示这个文件的基本属性,比如是否是可执行文件,对应的 CPU、操作系统等等。除了这些基本属性之外,
大部分程序还有这么一些 Section
- 首先是.text Section,也叫作代码段或者指令段(Code Section),用来保存程序的代码和指令;
- 接着是.data Section,也叫作数据段(Data Section),用来保存程序里面设置好的初始化数据信息;
- 然后就是.rel.text Secion,叫作重定位表(Relocation Table)。重定位表里,保留的是当前的文件里面,哪些跳转地址其实是我们不知道的。比如上面的 link_example.o 里面,我们在 main 函数里面调用了 add 和 printf 这两个函数,但是在链接发生之前,我们并不知道该跳转到哪里,这些信息就会存储在重定位表里;
- 最后是.symtab Section,叫作符号表(Symbol Table)。符号表保留了我们所说的当前文件里面定义的函数名称和对应地址的地址簿。
链接器会扫描所有输入的目标文件,然后把所有符号表里的信息收集起来,构成一个全局的符号表。然后再根据重定位表,把所有不确定要跳转地址的代码,根据符号表里面存储的地址,进行一次修正。最后,把所有的目标文件的对应段进行一次合并,变成了最终的可执行代码。这也是为什么,可执行文件里面的函数调用的地址都是正确的。
在链接器把程序变成可执行文件之后,要装载器去执行程序就容易多了。装载器不再需要考虑地址跳转的问题,只需要解析 ELF 文件,把对应的指令和数据,加载到内存里面供 CPU 执行就可以了
为什么同样一个程序,在 Linux 下可以执行而在 Windows 下不能执行了。
其中一个非常重要的原因就是,两个操作系统下可执行文件的格式不一样。
Linux 下的 ELF 文件格式,而 Windows 的可执行文件格式是一种叫作 PE(Portable Executable Format)的文件格式。
Linux 下的装载器只能解析 ELF 格式而不能解析 PE 格式。如果我们有一个可以能够解析 PE 格式的装载器,我们就有可能在 Linux 下运行 Windows 程序了。
这样的程序真的存在吗?没错,Linux 下著名的开源项目 Wine,就是通过兼容 PE 格式的装载器,使得我们能直接在 Linux 下运行 Windows 程序的。而现在微软的 Windows 里面也提供了 WSL,也就是 Windows Subsystem for Linux,可以解析和加载 ELF 格式的文件。
我们去写可以用的程序,也不仅仅是把所有代码放在一个文件里来编译执行,而是可以拆分成不同的函数库,最后通过一个静态链接的机制,使得不同的文件之间既有分工,又能通过静态链接来“合作”,变成一个可执行的程序。
对于 ELF 格式的文件,为了能够实现这样一个静态链接的机制,里面不只是简单罗列了程序所需要执行的指令,还会包括链接所需要的重定位表和符号表。推荐阅读想要更深入了解程序的链接过程和 ELF 格式,
推荐阅读《程序员的自我修养——链接、装载和库》的 1~4 章。这是一本难得的讲解程序的链接、装载和运行的好书
参考:
深入浅出计算机组成原理