GCC工具概念和简单使用

1 GCC编译工具链

GCC编译工具链(toolchain),是指以GCC编译器为核心的一整套工具。它主要包含以下三部分内容:

  • gcc-core:即GCC编译器,用于完成预处理和编译过程,把C代码转换成汇编代码。
  • Binutils :除GCC编译器外的一系列小工具包括了链接器ld,汇编器as、目标文件格式查看器readelf等。
  • glibc:包含了主要的 C语言标准函数库,C语言中常常使用的打印函数printf、malloc函数就在glibc 库中。

1.1 Binutils工具集

  • as:汇编器,把汇编语言代码转换为机器码(目标文件)。
  • ld:链接器,把编译生成的多个目标文件组织成最终的可执行程序文件。
  • readelf:可用于查看目标文件或可执行程序文件的信息。
  • nm : 可用于查看目标文件中出现的符号。
  • objcopy: 可用于目标文件格式转换,如.bin 转换成 .elf 、.elf 转换成 .bin等。
  • objdump:可用于查看目标文件的信息,最主要的作用是反汇编。
  • size:可用于查看目标文件不同部分的尺寸和总尺寸,例如代码段大小、数据段大小、使用的静态内存、总大小等。

1.2 glibc库

glibc库是GNU组织为GNU系统以及Linux系统编写的C语言标准库,因为绝大部分C程序都依赖该函数库,该文件甚至会直接影响到系统的正常运行,例如常用的文件操作函数read、write、open,打印函数printf、动态内存申请函数malloc等。

2 实践

2.1 编写HelloWorld文件

#include <stdio.h>
int main()
{
printf("hello, world! \n");
for(int i=0;i<10;i++ ){
printf("output i=%d\n",i);
}
return 0;
}

2.2 编译并执行

Ubuntu默认安装GCC编译工具链,写好程序后可以直接进行编译,执行以下命令

# 在Ubantu的hello_c目录下执行如下命令
gcc hello.c –o hello #使用gcc把hello.c编译成hello程序

ls #查看目录下的文件
./hello #执行生成的hello程序

# 若提示权限不够或不是可执行文件,执行如下命令再运行hello程序
chmod u+x hello #给hello文件添加可执行权限

3 具体过程

GCC 编译工具链在编译一个C源文件时需要经过以下 4 步:

  • 预处理:为把头文件的代码、宏之类的内容转换成生成的.i文件,还是C代码。
  • 编译:把预处理后的.i文件通过编译成.s文件,汇编语言。
  • 汇编:将汇编语言文件生成目标文件.o文件,机器码。
  • 链接:将每个源文件对应的.o文件链接起来,就生成一个可执行程序文件。

3.1 预处理阶段

预处理过程中,对源代码文件中的文件包含 (include)、 预编译语句 (如宏定义define等)进行展开,生成 .i 文件。 可理解为把头文件的代码、宏之类的内容转换成更纯粹的C代码,不过生成的文件以.i为后缀。

gcc –E hello.c –o hello.i

3.2 编译阶段

把预处理后的.i文件通过编译成为汇编语言,生成.s文件,即把代码从C语言转换成汇编语言,这是GCC编译器完成的工作。在这个过程,GCC会检查各个源文件的语法,即使我们调用了一个没有定义的函数,也不会报错。

GCC可以使用-S选项,让编译程序生成汇编语言的代码文件(.s后缀)。

# 编译,可理解为把C代码转换为汇编代码,把*.i转换得到*.s文件
gcc –S hello.i –o hello.s

# 也可以直接以C文件作为输入进行编译,与上面的命令是等价的
gcc –S hello.c –o hello.s

3.3 汇编阶段

将汇编语言文件经过汇编,生成目标文件.o文件,每一个源文件都对应一个目标文件。即把汇编语言的代码转换成机器码,这是as汇编器完成的工作。

GCC的参数“c”表示只编译(compile)源文件但不链接,会将源程序编译成目标文件(.o后缀)。计算机只认识0或者1,不懂得C语言,也不懂得汇编语言,经过编译汇编之后,生成的目标文件包含着机器代码,这部分代码就可以直接被计算机执行。一般情况下,可以直接使用参数“c”,跳过上述的两个过程,具体命令 如下:

# 汇编,可理解为把汇编代码转换为机器码,把*.s转换得到*.o,即目标文件
gcc –c hello.s –o hello.o

# 也可以直接以C文件作为输入进行汇编,与上面的命令是等价的
gcc –c hello.c –o hello.o

3.4 链接阶段

最后将每个源文件对应的目标.o文件链接起来,就生成一个可执行程序文件,这是链接器ld完成的工作。
这里分为动态链接和静态链接。

  • 动态链接:GCC编译时的默认选项。动态是指在应用程序运行时才去加载外部的代码库,不同的程序可以共用代码库。 所以动态链接生成的程序比较小,占用较少的内存。
  • 静态链接:链接时使用选项 “–static”,它在编译阶段就会把所有用到的库打包到自己的可执行程序中。 所以静态链接的优点是具有较好的兼容性,不依赖外部环境,但是生成的程序比较大。
    # 在hello.o所在的目录执行如下命令
    # 动态链接,生成名为hello的可执行文件

    gcc hello.o –o hello

    # 也可以直接使用C文件一步生成,与上面的命令等价
    gcc hello.c -o hello

    # 静态链接,使用--static参数,生成名为hello_static的可执行文件
    gcc hello.o –o hello_static --static

    # 也可以直接使用C文件一步生成,与上面的命令等价
    gcc hello.c -o hello_static --static

4 end

gcc的更多指令可以根据gcc --help或google查找。