交叉编译
在嵌入式系统开发中,经常会听到一个词:交叉编译。到底什么是"交叉编译"呢?为什么要使用"交叉编译"呢?今天这篇文章,我们来讨论下这个话题。
在讨论交叉编译之前,我们先来聊聊编译。
1、编译
在程序开发中,使用高级语言编写的代码被称为源代码,比如用C语言编写的后缀名为.c的文件,或者C++编写的后缀名为.cpp的文件。源代码不能被机器执行,必须转换成二进制的机器代码(指令+数据)才能被CPU执行。将源代码转换成机器代码的过程称为编译(Compile),编译的工作需要编译器(Complier)来完成。
编译器对源代码进行语法检查,只有没有语法错误的源代码才能被编译通过。源代码经过编译后,并没有生成最终的可执行文件,而是生成一种被称为目标文件(Object File)的中间文件。比如,Visual C++的目标文件后缀名为.obj,而GCC的目标文件后缀名为.o
源代码可能包含多个源文件,比如main.c/fun1.c/fun2.c等等,编译器会对源文件逐个进行编译。因此,有几个源文件,就会生成几个目标文件;
目标文件并不能被执行,因为它可能存在一些问题,比如源文件之间的引用关系导致的问题。
举个例子:文件A.c引用了文件B.c中的变量"EXT_someflag",A.c和B.c分别编译生成A.o和B.o,A.o中并没有变量"EXT_someflag"的定义,必须依靠B.o才能形成完整的代码。
同样的情况表现在源代码对库函数的引用。
把经过编译后生成的目标文件,按照其内在引用关系彼此相连接而生成一个完整的、可执行的文件的过程称为链接。
链接工作由链接器完成。
因此,源文件生成可执行文件要经过编译和链接两个步骤才能完成。为了方便,我们也把这个过程统称为编译。本文的主题"交叉编译"也是包含了编译和链接的步骤,不再赘述。
与交叉编译相对应的是"本地编译(native compile")。
理解本地编译有助于更好地理解交叉编译,所以,我们先来看看什么是本地编译?
2、本地编译
所谓"本地编译",是指编译源代码的平台和执行源代码编译后程序的平台是同一个平台。这里的平台,可以理解为CPU架构+操作系统。比如,在Intel x86架构/Windows 10平台下、使用Visual C++编译生成的可执行文件,在同样的Intel x86架构/Windows 10下运行。
3、交叉编译
所谓"交叉编译(Cross_Compile)",是指编译源代码的平台和执行源代码编译后程序的平台是两个不同的平台。比如,在Intel x86架构/Linux(Ubuntu)平台下、使用交叉编译工具链生成的可执行文件,在ARM架构/Linux下运行。
交叉编译是相对复杂的,必须考虑如下几个问题:
1.CPU架构:比如ARM,x86,MIPS等等;
2.字节序:大端(big-endian)和小端(little-endian);
3.浮点数的支持;
4.应用程序二进制接口(Application Binary Interface,ABI);
为什么要使用交叉编译呢?主要有两个原因:
1.交叉编译的目标系统一般都是内存较小、显示设备简陋甚至没有,没有能力在其上进行本地编译;
2.有能力进行源代码编译的平台CPU架构或操作系统与目标平台不同;
交叉编译工具链是进行交叉编译的必不可少的工具,是嵌入式开发人员必须熟练掌握的技能。我们会在后续文章中对交叉编译工具链进行介绍。