Linux如何用编译器编译源码-2、Linux下程序代码的编译

1. 编译工具前言

1)编译器和目标程序运行在同一架构上的编译过程称为本地编译。

2)编译器和目标程序运行在不同架构上的编译过程称为交叉编译

为什么需要交叉编译? 因为编译过程往往需要大量计算能力空间,而这个要求在arm平台上往往无法满足,所以需要在x86上编译然后下载到arm上运行。

1. 海合会简介

GCC(GNU Compiler Collection)用于在Linux下编译C代码。 GCC(GNU Compiler Collection)是GNU组织开发的一种编程语言编译器。 它最初被称为GNU C Compiler,仅支持编译C语言。 后来逐渐开始支持编译C++、Java等语言,因此更名为GNU Compiler Collection,缩写仍为GCC。 C语言的GCC由以下部分组成

1)gcc-coregcc编译器,可以将源代码编译成可执行程

2)Binutils:gcc-core以外的小工具集合包括链接器ld、汇编器as等。

3)glibc:C语言标准库,包括printf函数

执行以下命令检查gcc编译的目标程序是否运行在x86或arm上:

gcc -v

输出gcc的版本信息,其中包含item目标,根据该item的值来确定

1)target:x86_64-linux-gnu表示编译生成的程序只能在x86架构上运行。
2)target:arm-linux-gnueabihf,表示编译生成的程序只能在arm架构上运行。

2.GCC编译过程

使用gcc编译代码的基本句型如下:

gcc [选项] 输入文件

常用选项如下:
-o:小写字母“o”,指定生成的可执行文件的名字,不指定的话生成的可执行文件名为 a.out
-E:只进行处理,既不编译,也不汇编,只是将include的文件和宏定义展开
-S:只编译,不汇编
-c:编译并汇编,但不进行链接
-g:生成的可执行文件带调试信息,方便使用 gdb 进行调试
-Ox:大写字母“O”加数字设置程序的优化等级,如“-O0”“-O1”“-O2”“-O3”,数字
越大代码的优化等级越高,编译出来的程序一般会越小,但有可能会导致程序不正常运行

了解以上选项Linux如何用编译器编译源码,首先要知道gcc编译代码的几个阶段:预处理、编译、汇编、链接,如下:

下面以一个例子来说明上述过程,假设有一个hello.c源文件:

#include 
int main()
{
        printf("hello, world! This is a C program.n");
        for(int i = 0; i < 10; i++)
        {
                printf("output i = %dn", i);
        }
        return 0;
}

然后执行以下命令,直接从源码一步生成并执行可执行文件:

gcc hello.c -o hello
./hello

事实上,gcc hello.c -o hello命令包含以下过程:

# 1.预处理,可理解为把头文件和宏的代码展开汇总成 C 代码,把 *.c 转换得到 *.i 文件
gcc –E hello.c –o hello.i
# 2.编译,可理解为把 C 代码转换为汇编代码,把 *.i 转换得到 *.s 文件
gcc –S hello.i –o hello.s
# 3.汇编,可理解为把汇编代码转换为机器码,把 *.s 转换得到 *.o,即目标文件
gcc –c hello.s –o hello.o
# 4.链接,把不同文件之间调用关系链接起来,把一个或多个 *.o 转换成最终的可执行文件
gcc hello.o –o hello

1)以上四个过程可以单独执行Linux如何用编译器编译源码,也可以按照顺序组合在一起执行,如:

gcc -S hello.c -o hello.s    #相当与1和2两条命令的结合,同理
gcc –c hello.c –o hello.o   #相当于1,2,3这三条命令的结合

2)上述过程生成的文件可以打开。 比如执行 gcc -E hello.c -o hello.i ,就可以直接用 vim 打开 hello.i ,就会看到相关的头文件代码都包含进来了,而且宏也被替换掉了并扩大。

3)执行gcc –S hello.i –o hello.s并打开hello.s,可以看到生成的汇编代码。

4)不过需要注意的是,执行gcc -c hello.s -o hello.o后,如果用vim打开hello.o,会发现代码是乱码,因为.o文件是机器码,而是要被机器读取的。 .o 文件可以使用 readelf 工具打开:

readelf -a hello.o

命令执行结果如下:

从上图可以看到,目标文件包含ELF头、程序头、节等。对于*.o目标文件和*.so库文件,链接器根据这些内容信息生成可执行程序。 对于可执行文件,系统会在运行时根据这些信息加载程序。

注意:Linux下生成的.o目标文件、.so动态库文件、可执行文件等都是elf格式,可以通过readelf工具查看内容。

3. 链接过程

链接有两种类型静态链接和动态链接。

动态链接:使用 GCC 编译时的默认选项。 动态是指应用程序运行时加载外部代码库,例如C语言的标准库。 如果是动态链接生成的程序,那么应用程序运行时就会加载它。

静态链接:链接时使用选项“-static”,它将在编译阶段将所有使用的库打包成自己的可执行程序。

静态链接的优点兼容性好,程序基本不依赖于外部环境,但生成的程序也比较大。

动态链接生成的程序兼容较差,但生成的程序较小。

gcc hello.c -o hello   #动态链接生成
gcc hello.c -o hello_static --static   #静态链接生成

通过比较,发现动态链接生成的hello比静态链接生成的hello_static小很多。

4. 交叉编译器简介

编译器和目标程序运行在不同的体系结构上。 这种编译器称为交叉编译器。 x86平台上主要的gcc交叉编译器包括:

1)arm-linux-gnueabihf-gcc:该编译器名称中的Linux表示该编译器生成的目标程序运行在Linux系统上,该程序可以使用Linux下的c标准库和Linux内核API。

2)arm-none-eabi-gcc:名称中的none表示该编译器生成的目标程序的运行环境不需要操作系统,可以用来编译逻辑代码、uboot、内核代码等。

请注意,arm-linux-gnueabihf-gcc 还可以编译 uboot 和内核代码。 但建议使用arm-none-eabi-gcc。

以上两个编译器都是Linaro组织提供的。 Linaro 是一个由 ARM 发起并与其他 ARM SOC 公司共同投资的非盈利组织。 大多数开发人员使用Linaro组织提供的交叉编译器。

Linaro组织的编译器命名规则如下:

arch [-os] [-(gnu)eabi(hf)] -gcc

其中,hf表示编译器的浮点模式为hard-float(使用fpu参与浮点运算)。 如果没有hf,说明是soft-float(即使有fpu浮点单元,也没有使用,正在使用软件模式)。

**注意,** 如果目标平台的c库文件(如glibc库文件libc.so.6)都是硬浮点类型,那么使用不带hf的编译器编译出来的目标程序可能会很难编译在Linux平台上运行。 使用以下命令查看库类型:

readelf -h libc.so.6

假设平台A的输出如下:

如您所见,平台 A 库类型是硬浮动的。 如果使用如下命令进行编译:

sudo arm-linux-gnueabi-gcc  hello.c -o hello

那么编译生成的hello在平台A上运行失败,因为平台A的c库文件是硬浮点型的,而hello是由软浮点型编译器编译的。

要在平台A上运行,有两种方法方法一是使用硬浮点类型编译器。

sudo arm-linux-gnueabihf-gcc hello.c -o hello_hf

第二种方法是使用静态编译:

sudo arm-linux-gnueabi-gcc hello.c -o hello_static  --static

那么编译生成的hello_hf和hello_static都可以在平台A上运行。

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

悟空资源网 源码编译 Linux如何用编译器编译源码-2、Linux下程序代码的编译 https://www.wkzy.net/game/201739.html

常见问题

相关文章

官方客服团队

为您解决烦忧 - 24小时在线 专业服务