ARM_Linux中GCC编译器的使用

目录

前言:

GCC编译过程:

预处理:

编译阶段:

汇编:

链接阶段

GCC的常见使用


前言:

什么是GCC:

gcc的全称是GNU Compiler Collection,它是一个能够编译多种语言的编译器。最开始gcc是作为C语言的编译器(GNU C Compiler),现在除了c语言,还支持C++、java、Pascal等语言。gcc支持多种硬件平台。

特点:

  • gcc是一个可移植的编译器,支持多种硬件平台。例如ARM、X86等等。
  • gcc不仅是个本地编译器,它还能跨平台交叉编译。所谓的本地编译器,是指编译出来的程序只能够在本地环境进行运行。而gcc编译出来的程序能够在其他平台进行运行。例如嵌入式程序可在x86上编译,然后在arm上运行。
  • gcc有多种语言前端,用于解析不同的语言。
  • gcc是按模块化设计的,可以加入新语言和新CPU架构的支持。
  • gcc是自由软件。任何人都可以使用或更改这个软件。

        源文件需要经过编译才能够生成可执行文件,在Windows下进行开发的时候,只需要几个按键就可以编译,集成的开发环境(V Studio,keil,clion等)已经将各种编译工具封装好了,linux中虽然有一些集成好的编译器,但是对于嵌入式软件开发的时候,通常没有屏幕,只能通过指令进行程序的编写和调试。但是要编译出来的程序可以在ARM上运行,就必须使用交叉编译器xxx-gcc等。

GCC编译过程:

一个c/c++文件要经过四个过程才能变成可执行文件。

  • 预编译(Pre-Processing)
  • 编译(Compiling)
  • 汇编(Assembling)
  • 链接(Linking)

ARM_Linux中GCC编译器的使用_第1张图片

预处理:

预处理过程中,是对源代码文件中的包含(include)、预编译语句(以#开头的命令等)进行展开生成.i文件。

使用GCC的参数 “-E”,可以让编译器生成 .i 文件,参数 “-o”,可以指定输出文件的名字。

 # 预处理,可理解为把头文件的代码汇总成C代码,把*.c转换得到*.i文件
 gcc –E test1.c –o test1.i

ARM_Linux中GCC编译器的使用_第2张图片

ARM_Linux中GCC编译器的使用_第3张图片

预处理就是将要包含“#include”的文件插入到原文件中,将宏定义展开,根据条件编译命令选择要使用的代码,最后将这些东西输入到“.i”文件中等待进一步处理

编译阶段:

gcc调用不同语言的编译器,例如c语言调用编译器ccl。gcc实际上是个工具链,在编译程序的过程中调用不同的工具。而在编译阶段就是通过调用的工具将预处理后的.i文件编译成汇编语言,生成.s文件,就是把代码从C语言转为汇编语言。在这个过程,GCC会检查各个源文件的语法。

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

 # 编译,可理解为把C代码转换为汇编代码,把*.i转换得到*.s文件
 gcc –S hello.i –o hello.s
 ​
 # 也可以直接以C文件作为输入进行编译,与上面的命令是等价的
 gcc –S hello.c –o hello.s

ARM_Linux中GCC编译器的使用_第4张图片

了解过汇编语言的应该知道,对于不同的芯片会由不同的汇编指令集,这个是没法通用的。所以这是为什么要使用交叉编译器的一个主要原因。

汇编:

汇编阶段,gcc调用汇编器进行汇编。将汇编语言的.s文件经过汇编生成.o文件,每一个源文件都对应一个目标文件。把汇编语言转换成机器码。

 # 汇编,可理解为把汇编代码转换为机器码,把*.s转换得到*.o,即目标文件
 gcc –c hello.s –o hello.o
 ​
 # 也可以直接以C文件作为输入进行汇编,与上面的命令是等价的
 gcc –c hello.c –o hello.o

双击.o文件是打不开的,使用vim打开也是一堆乱码,这是因为Linux下生成的.o目标文件、.so动态库文件以及可执行文件都是elf格式的。可以使用“readelf”工具来查看内容。

ARM_Linux中GCC编译器的使用_第5张图片

ARM_Linux中GCC编译器的使用_第6张图片

从 readelf 的工具输出的信息,可以了解到目标文件包含ELF头、程序头、节等内容, 对于*.o目标文件或*.so库文件,编译器在链接阶段利用这些信息把多个文件组织起来。

链接阶段

最后将每个源文件对应的目标文件.o链接起来,就生成一个成执行文件了。

例如一个工程里包含了A和B两个代码文件,在链接阶段, 链接过程需要把A和B之间的函数调用关系理顺,也就是说要告诉A在哪里能够调用到fun函数, 建立映射关系,所以称之为链接。若链接过程中找不到fun函数的具体定义,则会链接报错。

虽然文章的test1例子中只有一个.c文件,但是调用了C标准代码库中的printf函数, 所以链接器会把它和printf函数链接起来,生成最终的可执行文件。

链接分为两种:

  • 动态链接: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

ARM_Linux中GCC编译器的使用_第7张图片

从图中可以看到,使用动态链接生成的hello程序才16.6KB, 而使用静态链接生成的hello_static程序则高达871.9KB。

在Ubuntu下,可以使用 ldd 工具查看动态文件的库依赖,尝试执行如下命令:

但是静态链接生成的可执行文件没有依赖外部库文件。

GCC的常见使用

//gcc使用示例
gcc hello.c // 输出一个名为 a.out的可执行程序,然后可以执行./a.out
gcc -o hello hello.c // 输出名为 hello的可执行程序,然后可以执行./hello
gcc -o hello hello.c -static // 静态链接
gcc -c -o hello.o hello.c // 先编译(不链接)
gcc -o hello hello.o // 再链接

执行“gcc -o test1 test1.c -v”的时候可以看到这写步骤

常用编译选项:

ARM_Linux中GCC编译器的使用_第8张图片

多文件编译

//一次性编译
gcc -o main  hello.c main.c //生成可执行文件main
//独立编译
gcc -Wall -c-o main.o main.c 
gcc -Wall -c -o hello.o hello.c
gcc -Wall -o main main.o hello.o

制作动态库:

ARM_Linux中GCC编译器的使用_第9张图片

静态库:

ARM_Linux中GCC编译器的使用_第10张图片

文章参考:

GCC基本使用 - 知乎 (zhihu.com)

Linux编译工具:gcc入门 - melonstreet - 博客园 (cnblogs.com)

你可能感兴趣的:(Linux_study,arm开发,linux,gcc)