linux开发工具:gcc

GCC是GNUCompilerCollection的简称,它是一个自由软件编译器,支持多种编程语言和硬件平台。GCC的编译过程包括预处理、编译、汇编和链接四个步骤。文章还介绍了GCC的常用编译选项,如何处理多个源文件,以及如何生成和使用静态库与动态库。此外,讨论了ARM架构的交叉编译,强调了交叉工具链在嵌入式开发中的重要性。

1:GCC简介

GCC(GNU Compiler Collection)编译器在1987年发布了第一个C语言版本,它是用GPL许可证发行的自由软件,也是GNU计划的关键部分。GCC现在是GNU Linux操作系统的默认编译器,同时也被很多自由软件采用。GCC 在后续的发展过程中,扩展支持了很多的编程语言,如C++、Java、Go等语言。另外,GCC还支持多种不同的硬件平台,如x86、ARM等体系结构。

2:GCC编译过程

GCC的编译流程主要分成4个步骤。

预处理(Pre-Process)
编译(Compile)
汇编(Assemble)
链接(Link)

以常用的一个hello.c程序的编译为例:

  1. GCC的预处理器(cpp)进行预处理,把头文件、宏等进行展开,生成hello.i文件。
  2. 进入GCC的编译器,由于GCC可以支持多种编程语言,这里调用C语言的编译器ccl。
  3. 编译完成生成汇编程序,输出hello.s文件。
  4. 汇编阶段,GCC调用汇编器(as)进行汇编,生成可重定位的目标程序。
  5. 链接,GCC调用链接器把所有目标文件和C语言的标准库链接成可执行的二进制文件。
    ![[Pasted image 20230719210031.png]]

3:GCC常用选项

gcc常用选项:

选项名作用
-o产生目标(.i、.s、.o、可执行文件等)
-E只运行C预编译器
-S告诉编译器产生汇编程序文件后停止编译,产生的汇编语言文件拓展名为.s
-c通知gcc取消连接步骤,即编译源码,并在最后生成目标文件
-Wall使gcc对源文件的代码有问题的地方发出警告
-Idir将dir目录加入搜索头文件的目录路径
-Ldir将dir目录加入搜索库的目录路径
-llib连接lib库
-g在目标文件中嵌入调试信息,以便gdb之类的调试程序调试

现在我们有源文件hello.c,下面是一些gcc的使用示例:

gcc -E hello.c -o hello.i

对hello.c文件进行预处理,生成了hello.i 文件

gcc -S hello.i -o hello.s

对预处理文件进行编译,生成了汇编文件

gcc -c hello.s -o hello.o

对汇编文件进行编译,生成了目标文件

gcc hello.o -o hello

对目标文件进行链接,生成可执行文件

gcc hello.c -o hello

直接编译链接成可执行目标文件

gcc -c hello.cgcc -c hello.c -o hello.o

编译生成可重定位目标文件

使用gcc时可以加上-Wall选项。下面这个例子如果不加上-Wall选项,编译器不会报出任何错误或警告,但是程序的结果却不是预期的:

//bad.c
#include<stdio.h>
int main()
{
    printf("the number is %f ",5);  //程序输出了the number is 0.000000,结果错误
    return 0;
 }

使用-Wall选项:

gcc -Wall bad.c -o bad

gcc将输出警告信息:

warning: format ‘%f’ expects argument of type ‘double’, but argument 2 has type ‘int’ [-Wformat=]
printf(“the number is %f\n”,5);

4:GCC 编译多个文件

假设现在有三个文件:hello.c hello.h main.c ,三个文件的内容如下:

// hello.c
#include<stdio.h>
#include"hello.h"
void printHello()
{
     printf("hello world!\n");
}
//main.c
#include<stdio.h>
#include"hello.h"
int main()
{
        printHello();
        return 0;
}
//hello.h
//仅包含函数声明
#ifndef _HELLO_
#define _HELLO_
void printHello();
#endif

编译这三个文件,可以一次编译:

gcc hello.c main.c -o main 生成可执行文件main

也可以独立编译:

gcc -Wall -c main.c -o main.o
gcc -Wall -c hello.c -o hello.o
gcc -Wall main.o hello.o -o main

独立编译的好处是,当其中某个模块发送改变时,只需要编译该模块就行,不必重新编译所有文件,这样可以节省编译时间。

5:GCC使用外部库

在使用C语言和其他语言进行程序设计的时候,我们需要头文件来提供对常数的定义和对系统及库函数调用的声明。库文件是一些预先编译好的函数集合,那些函数都是按照可重用原则编写的。它们通常由一组互相关联的可重用原则编写的,它们通常由一组互相关联的用来完成某项常见工作的函数构成。使用库的优点在于:

  • 模块化的开发
  • 可重用性
  • 可维护性
    库又可以分为静态库与动态库:
  • 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库。静态库比较占用磁盘空间,而且程序不可以共享静态库。运行时也是比较占内存的,因为每个程序都包含了一份静态库。
  • 动态库(.so或.sa):程序在运行的时候才去链接共享库的代码,多个程序共享使用库的代码,这样就减少了程序的体积。
    一般头文件或库文件的位置在:
/usr/include及其子目录底下的include文件夹
/usr/local/include及其子目录底下的include文件夹
/usr/lib
/usr/local/lib
/lib

5.1:生成静态库

为了生成.a文件,我们需要先生成.o文件。下面这行命令将我们的hello.o打包成静态库libhello.a:

ar rcs libhello.a hello.o

ar是gun归档工具,rcs表示replace and create,如果libhello之前存在,将创建新的libhello.a并将其替换。

然后就可以这样来使用静态库libhello.a

gcc -Wall main.c libhello.a -o main

还有另外一种使用方式:

gcc -Wall -L. main.c -o main -lhello 【lhello 是 libhello的缩写】

其中 -L. 表示库文件的位置在当前目录下,由于libhello.a是我们自己生成的,并存放在当前录下下,所以需要加上-L.选项。默认库文件是在系统的目录下进行搜索。同样的,-I.选项用于头文件的搜索。

5.2:生成动态库

生成一个共享库,名称的规则是libxxx.so。将刚才hello.o生成libhello.so的命令为:

gcc -shared -fPIC hello.o -o libhello.so

生成了共享库之后,可以这样来使用共享库:

gcc -Wall main.o -o main -L. -lhello

该命令与使用静态库的命令相同,但是在共享库与静态库共存的情况下,优先使用共享库。

共享库有时候并不不在当前的目录下,为了让gcc能够找得到共享库,有下面几种方法:

  1. 拷贝.so文件到系统共享库路径下,一般指/usr/lib
  2. 在~/.bash_profile文件中,配置LD_LIBRARY_PATH变量
  3. 配置/etc/ld.so.conf,配置完成后调用ldconfig更新ld.so.cache

其中,shared选项表示生成共享库格式。fPIC表示产生位置无关码(position independent code),位置无关码表示它的运行、加载与内存位置无关,可以在任何内存地址进行加载。

库的搜索路径遵循几个搜索原则:从左到右搜索-I -l指定的目录,如果在这些目录中找不到,那么gcc会从由环境 变量指定的目录进行查找。头文件的环境变量是C_INCLUDE_PATH,库的环境变量是LIBRARY_PATH.如果还是找不到,那么会从系统指定指定的目录进行搜索。

6:交叉编译 ARM GCC

GCC 具有良好的可扩展性,除了可以编译 x86 体系结构的二进制程序外,还可以支持很多其他体系结构的处理器,如 ARM、MIPS、RISC-V 等。
这里涉及两个概念,一个是本地编译,另一个是交叉编译
本地编译:在当前目标平台编译出来的程序,并且可以运行在当前平台上。
交叉编译:在一种平台上编译,然后放到另一种平台上运行,这个过程称为交叉编译。之所以有交叉编译,主要是因为嵌入式系统的资源有限,不适合在嵌入式系统中进行编译,如早期ARM处理器性能低下,要编译一个完整的Linux系统是不现实的。因此,首先会在某个高性能的计算机上编译出能在ARM处理器运行的Linux二进制文件,然后烧录到ARM系统中运行。

交叉工具链:交叉工具链不只是GCC,还包含binutils、glibc等工具组成的综合开发环境,可以实现编译、链接等功能。在嵌入式环境中,通常使用uclibc等小型的C语言库。

交叉工具链的命名规则一般如下。
[arch] [-os] [-(gnu)eabi]
arch:表示体系结构,如ARM、MIPS等。
os:表示目标操作系统。
eabi:嵌入式应用二进制接口。

许多Linux发行版提供了编译好的ARM GCC的工具链,如优麒麟Linux 18.04上提供如下和ARM相关的编译器。

arm-linux-gnueabi:主要用于基于ARM32架构的Linux系统,可以用来编译ARM32架构的u-boot、Linux内核以及Linux应用程序等。优麒麟Linux 18.04系统中提供了GCC 5、GCC 6、GCC 7以及GCC 8等多个版本。
aarch-linux-gnueabi:主要用于基于ARM64架构的Linux系统。

arm-linux-gnueabihf:hf指的是支持硬件浮点(Hard Float)的ARM处理器。
在之前的一些ARM处理器中不支持硬件浮点单元,所以由软件浮点来实现。但是最新的一些高端ARM处理器内置了硬件浮点单元,这样新旧两种架构的差异就产生了两个不同的EABI接口。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值