GCC编译

GCC(英文全拼:GNU Compiler Collection)是 GNU 工具链的主要组成部分,是一套以 GPL 和 LGPL 许可证发布的程序语言编译器自由软件,由 Richard Stallman 于 1985 年开始开发。

GCC 原名为 GNU C语言编译器,因为它原本只能处理 C 语言,但如今的 GCC 不仅可以编译 C、C++ 和 Objective-C,还可以通过不同的前端模块支持各种语言,包括 Java、Fortran、Ada、Pascal、Go 和 D 语言等等。

GCC 的编译过程可以划分为四个阶段:预处理(Pre-Processing)、编译(Compiling)、汇编(Assembling)以及链接(Linking)

img

 #include <stdio.h>
 #define HH 5
 void foo(void)
 {
     printf("Here is a static library\n");
     printf("%d\n",HH);
 }

预处理

主要是处理程序中以#开头的预处理指令,将其转换后插入文本中。

gcc -e hello.c -o hello.i
  • 递归处理#include预处理指令,将对应文件的内容复制到该处,出现了很多extern的函数原型
  • 删除#define,并且在其被引用处递归地展开所有的宏定义
  • 处理所有条件预处理指令 #if #ifdef
  • 删除注释
  • 添加行号和文件名标识

image-20211111181613056

编译

gcc -S hello.c -o hello.s
gcc -S hello.i -o hello.s -masm= intel -fno-asynchronous-unwind-tables

-masm= intel用于指定为intel格式

-fno-asynchronous-unwind-tables 用于生成没有cfi宏的汇编指令,提高可读性

可以看到只有单个参数的printf被替换成puts了。

	.file	"hello.c"
	.text
	.section	.rodata
.LC0:
	.string	"Here is a test of gcc"
.LC1:
	.string	"%d\n"
	.text
	.globl	foo
	.type	foo, @function
foo:
.LFB0:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	leaq	.LC0(%rip), %rdi
	call	puts@PLT
	movl	$5, %esi
	leaq	.LC1(%rip), %rdi
	movl	$0, %eax
	call	printf@PLT
	nop
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE0:
	.size	foo, .-foo
	.ident	"GCC: (Debian 10.2.1-6) 10.2.1 20210110"
	.section	.note.GNU-stack,"",@progbits

汇编

汇编器根据汇编指定和机器指令对应的表进行翻译,生成目标文件。

gcc -c hello.c -o hello.o
gcc -c hello.s -o hello.o

此时hello.o是一个可重定位文件

image-20211111182736299

使用objdump查看内容,可以发现此时由于没有链接,导致字符串的起始地址为0x0000,函数地址也用foo标识。

ocjdump -sd hello.o -M intel
hello.o:     文件格式 elf64-x86-64

Contents of section .text:
 0000 554889e5 488d3d00 000000e8 00000000  UH..H.=.........
 0010 be050000 00488d3d 00000000 b8000000  .....H.=........
 0020 00e80000 0000905d c3                 .......].       
Contents of section .rodata:
 0000 48657265 20697320 61207465 7374206f  Here is a test o
 0010 66206763 63002564 0a00               f gcc.%d..      
Contents of section .comment:
 0000 00474343 3a202844 65626961 6e203130  .GCC: (Debian 10
 0010 2e322e31 2d362920 31302e32 2e312032  .2.1-6) 10.2.1 2
 0020 30323130 31313000                    0210110.        
Contents of section .eh_frame:
 0000 14000000 00000000 017a5200 01781001  .........zR..x..
 0010 1b0c0708 90010000 1c000000 1c000000  ................
 0020 00000000 29000000 00410e10 8602430d  ....)....A....C.
 0030 06640c07 08000000                    .d......        

Disassembly of section .text:

0000000000000000 <foo>:
   0:   55                      push   rbp
   1:   48 89 e5                mov    rbp,rsp
   4:   48 8d 3d 00 00 00 00    lea    rdi,[rip+0x0]        # b <foo+0xb>
   b:   e8 00 00 00 00          call   10 <foo+0x10>
  10:   be 05 00 00 00          mov    esi,0x5
  15:   48 8d 3d 00 00 00 00    lea    rdi,[rip+0x0]        # 1c <foo+0x1c>
  1c:   b8 00 00 00 00          mov    eax,0x0
  21:   e8 00 00 00 00          call   26 <foo+0x26>
  26:   90                      nop
  27:   5d                      pop    rbp
  28:   c3                      ret    

链接

gcc默认使用动态链接,加入-static选项即可静态链接。将目标文件及其依赖库进行连接,生成可执行文件,主要包括 地址和空间分配符号绑定重定位

gcc hello.o -o hello -static

由于使用静态链接,导致objdump后存在大量汇编指令

参考

gcc 编译命令详解及最佳实践 - 知乎 (zhihu.com)

posted @ 2021-11-12 16:16  ddddd1234654732  阅读(409)  评论(0)    收藏  举报
Live2D