重新理解Linux交叉编译及编译流程

一、交叉编译背景

当我们开发目标是一个嵌入式设备时便需要在PC机上编译出能在该嵌入式设备上运行的可执行文件这里编译主机与目标运行主机不是同一个设备那么该过程就称为交叉编译而编译是指一个源代码文件这里指的是编译性程序源文件与之对应的是解释性程序如C/C++文件要经过预处理preprocessing)、编译(compilation)、汇编assembly)和链接(linking等4步才能变成可执行文件注意在日常交流中通常使用“编译”统称这4个步骤。

当我们在 Windows下利用IDE工具即集成开发环境比如 Visual studio、keil、IAR、Eclipse等等进行开发时只需要单击几个按钮即可编译因为IDE工具已经将各种编译工具的使用封装好了。

Linux下也有很优秀的集成开发工具但是更多的时候是直接使用编译工具比如嵌入式开发中运行在PC平台上的编译工具链为gcc、ld、objcopy、objdump等它们编译出来的程序在x86平台上运行。要编译出能在 ARM 平台上运行的程序则须使用交叉编译工具 arm-linux-gcc、arm-linux-ld、arm-linux-objcopy、arm-linux-objdump等PC与ARM的编译工具使用方法一致单纯就是名字不一样。

小插曲关于开发工具的使用搭建开发环境和熟悉工具使用是进行开发的第一步也是一道难关尤其面对多种多样的工具和环境让人抓狂其实这也无可厚非因为各种系统、产品本身具有自己的特点以及软件工具的限制知识产权显然要做到一个工具通吃是不现实的而一般在学习网络教程/公司企业工作时所使用的工具有可能会直接采用某官方发布的软件也有可能经过二次开发/封装而来面对这种情况我们其实不用去抗拒学习开发工具本身就是开发过程中必要的一部分熟练掌握一套工具后同类的工具上手也会很快的因此要积极对待。

上图更好理解
在这里插入图片描述

二、gcc和arm-linux-gcc的常用选项

1、查询gcc帮助

先来简单认识下编译工具的使用我们先看操作再看原理以gcc为例子(与arm-linux-gcc使用一致)查询使用帮助
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sVGTABYv-1647098293080)(C:\Users\Anson\AppData\Roaming\Typora\typora-user-images\image-20220312211258526.png)]

2、常用gcc选项介绍

gcc选项很多一般大型开发项目都会使用很多控制选项这里仅介绍简单常用选项

-v查看gcc编译器的版本显示gcc执行时的详细过程
-o <file>         Place the output into <file> 指定输出文件名为file这个名称不能跟源文件名同名
                           
-E                       Preprocess only; do not compile, assemble or link只预处理不会编译、汇编、链接
                           
-S                       Compile only; do not assemble or link只编译不会汇编、链接
                           
-c                       Compile and assemble, but do not link编译和汇编不会链接 

3、生成一个可执行文件的三种方法

以hello.c文件为例
在这里插入图片描述

1方式1

gcc -E -o hello.i hello.c
gcc -S -o hello.s hello.i
gcc -c -o hello.o hello.s
gcc -o hello hello.o

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j6Djo2jB-1647098293085)(C:\Users\Anson\AppData\Roaming\Typora\typora-user-images\image-20220312215503760.png)]
gcc会对.c文件默认进行预处理操作-c再来指明了编译、汇编从而得到*.o文件object file 即我们常说的目标文件再通过gcc -o hello hello.o将.o文件进行链接得到可执行应用程序。

小结
1输入文件的后缀名和选项共同决定gcc到底执行哪些操作。
2在编译过程中除非使用了-E、-S、-c选项(或者编译出错阻止了完整的编译过程)否则最后的步骤都是链接。

2方式2 - 开发项目中常用的方式

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jgbLRjoG-1647098293087)(C:\Users\Anson\AppData\Roaming\Typora\typora-user-images\image-20220312215956177.png)]

3方式3

gcc -o hello hello.c  输出hello然后./hello来执行该应用程序。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TQGKkt0b-1647098293087)(C:\Users\Anson\AppData\Roaming\Typora\typora-user-images\image-20220312220125123.png)]

注如果不指定输出文件名则默认生成a.out

gcc hello.c  输出一个a.out然后./a.out来执行该应用程序。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iuF8bn91-1647098293088)(C:\Users\Anson\AppData\Roaming\Typora\typora-user-images\image-20220312220207527.png)]

二、交叉编译的四个流程及实例说明

1、预处理preprocessing)

问什么是预处理

C/C++源文件中以“#“开头的命令被称为预处理命令如包含命令“#include”、宏定义命令“#define”、条件编译命令“#if”、“#ifdef”等。预处理就是将要包含include的文件插入原文件中、将宏定义展开、根据条件编译命令选择要使用的代码最后将这些代码输出到一个".i"文件也就是说还是源代码文件

问为什么需要预处理

一般预处理都是些简单的替换、拷贝和选择这些涉及多个文件预处理的结果是将每个源文件所需要的代码都放在自己文件里然后方便下一步处理ps因为编译时编译器每次读入一个文件输出一个文件不支持同时处理多个文件 - 来源于“编译原理”

对比hello.i与hello.c文件内容
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W3zylFPg-1647098293089)(C:\Users\Anson\AppData\Roaming\Typora\typora-user-images\image-20220312220805414.png)]
对比hello.i与hello.c大小
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uCFeUudT-1647098293091)(C:\Users\Anson\AppData\Roaming\Typora\typora-user-images\image-20220312220946568.png)]
明显hello.i大很多原因是从其他文件拷贝了很多代码过来。

2、编译(compilation)

问什么是编译

编译就是把C/C++代码比如上述的“.i”文件)翻译成汇编代码这部分涉及复杂的编译器原理有兴趣可以自行去看书深究

问为什么要翻译成汇编而不直接生成机器码

1、其一由于最开始的底层开发语言是汇编而高级语言是在底层语言基础上发展的自然而然会将成熟的工具汇编器利用起来同时实现软件分层可以有效地减弱编译器编写的复杂性“编译”所拆分的四大步骤也是如此道理

2、其二有一个好处是方便优化和调试汇编语言是机器指令的助记 符一个汇编指令就对应一条机器指令因此汇编语言更贴近机器特性因此比高级语言调试起来更有优势

查看hello.s文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JSnlKdLy-1647098293091)(C:\Users\Anson\AppData\Roaming\Typora\typora-user-images\image-20220312223635993.png)]

3、汇编assembly)

问什么是汇编

汇编注意这里的汇编指的是编译器的一个编译动作不是汇编语言是利用汇编器将第二步输出的汇编代码翻译成符合一定格式的机器代码就是我们熟悉的目标文件*.o在Linux系统上一般表现为ELF格式文件如果开发代码是汇编则汇编+链接就可以生成可执行文件了

查看hello.o需要用readelf工具查看
在这里插入图片描述

问什么是反汇编文件

反汇编文件是由可执行文件逆向解析而来内容是按照实际内存分布来排布的包含地址信息一般用来调试分析用非常有用

生成反汇编文件
在这里插入图片描述

查看
在这里插入图片描述

问汇编文件与反汇编文件的区别

反汇编文件比汇编文件多了调试信息如物理地址、机器码等而汇编文件单纯只是汇编代码

4、链接(linking

问什么是链接

链接就是将汇编生成的OBJ文件、系统库的OBJ文件、库文件链接起来即将各个ELF文件重新排序成一个ELF文件最终生成可以在特定平台运行的可执行程序。

查看hello需要用readelf工具
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fGs8jYuz-1647098293094)(C:\Users\Anson\AppData\Roaming\Typora\typora-user-images\image-20220312225906363.png)]

问什么是系统库文件

系统库文件一个应用程序要运行在系统上就需要系统标准启动文件提供给系统用的注意裸机bootloader、linux内核等程序是不能使用启动文件以及标准库文件因为启动文件和库文件的使用是需要系统支持。

一般gcc自动加入的系统标准启动文件有crt1.o、crti.o、crtbegin.o、crtend.o、crtn.o
对于一般应用程序这些启动是必需的。

通过查看gcc详细编译过程可以看到

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MEIwa5uz-1647098293094)(C:\Users\Anson\AppData\Roaming\Typora\typora-user-images\image-20220312231007888.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xhohe0DU-1647098293095)(C:\Users\Anson\AppData\Roaming\Typora\typora-user-images\image-20220312231106971.png)]

问什么是标准库文件

很好理解即库文件如果代码用到标准库函数而gcc集成了常用的库链接时自动检索加入

问什么是动态链接

动态链接使用动态链接库进行链接生成的程序在执行的时候需要加载所需的动态库才能运行。
动态链接生成的程序体积较小但是必须依赖所需的动态库否则无法执行。

gcc默认使用动态库链接

问什么是静态链接?

静态链接使用静态库进行链接生成的程序包含程序运行所需要的全部库可以直接运行
不过静态链接生成的程序体积较大。

gcc静态链接需加入选项-static
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oHJZ7ZQE-1647098293095)(C:\Users\Anson\AppData\Roaming\Typora\typora-user-images\image-20220312231622957.png)]
很明显静态链接比动态链接生成的可执行文件大很多。

最后编译原理还有很多值得深究的问题比如ELF格式是什么系统如何支持动态链接的边实践边学习由浅及深通过现象理解本质才能不急不躁。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: linux

“重新理解Linux交叉编译及编译流程” 的相关文章