前言
在嵌入式开发中,经常会提到交叉编译,那么什么是交叉编译?和我们本地编译有什么区别呢?怎么使用交叉编译工具呢?
在问题之前,我们先简单说明下编译,用本地编译来对比说明。
在程序开发中,使用高级语言编写的代码被称为源代码,比如用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才能形成完整的代码。
把经过编译后生成的目标文件,按照其内在引用关系彼此相连接而生成一个完整的、可执行的文件的过程称为链接,链接工作由链接器完成。
因此,源文件生成可执行文件要经过编译和链接两个步骤才能完成。为了方便,我们也把这个过程统称为编译。
交叉编译
本地编译(编译)
“本地编译”(通常称编译),是指编译源代码的平台和执行源代码编译后程序的平台是同一个平台(是在一个平台上生成在该平台上的可执行代码)这里的平台,可以理解为CPU架构+操作系统。比如,在Intel x86架构/Windows 10平台下、使用Visual C++编译生成的可执行文件,在同样的Intel x86架构/Windows 10下运行。
而交叉编译是在一个平台上生成另一个平台上的可执行代码。
比如:我们在windows上面编写C51代码,并编译成可执行代码,如xx.hex,是在c51上面运行,不是在windows上面运行。
再如:我们在ubuntu上面编写树莓派的代码,并编译成可执行代码,如a.out,是在树莓派上面运行,不是在ubuntu linux上面运行。
交叉编译是相对复杂的,必须考虑如下几个问题:
CPU架构:比如ARM,x86,MIPS等等;
字节序:大端(big-endian)和小端(little-endian);
浮点数的支持;
应用程序二进制接口(Application Binary Interface,ABI)。
为什么要交叉编译?
- 有时是因为目的平台上不允许或不能够安装我们所需要的编译器,而我们又需要这个编译器的某些特征。
- 有时是因为目的平台上的资源贫乏,无法运行我们所需要编译器。
- 有时又是因为目的平台还没有建立,连操作系统都没有,根本谈不上运行什么编译器。
(操作系统也是代码,也需要编译。)平台运行至少需要两个东西,bootloader(启动引导代码)以及操作系统核心。
宿主机和目标机
宿主机(host) :编辑和编译程序的平台,一般是基于X86的PC机,通常也被称为主机。
目标机(target):用户开发的系统,通常都是非X86平台。host编译得到的可执行代码在target上运行。
交叉编译工具
交叉编译需要使用交叉编译器、交叉编译工具链。
要进行交叉编译,我们需要在主机平台上安装对应的交叉编译工具链(cross compilation tool chain),然后用这个交叉编译工具链编译我们的源代码,最终生成可在目标平台上运行的代码。常见的交叉编译例子如下:
1、在Windows PC上,利用ADS(ARM开发环境),使用armcc编译器,则可编译出针对ARM CPU的可执行代码。
2、在Linux PC上,利用arm-linux-gcc编译器,可编译出针对Linux ARM平台的可执行代码。
3、在Windows PC上,利用cygwin环境,运行arm-elf-gcc编译器,可编译出针对ARM CPU的可执行代码。
交叉编译工具链的安装
交叉编译工具链去树莓派官网安装。网址:https://github.com/raspberrypi/
- 将压缩包放在工作目录。
我的是从共享文件夹拷贝到ubuntu工作目录
cp /mnt/hgfs/share/tools-master.zip .
- 解压
unzip tools-master.zip
- 配置环境变量
- 3.1配置临时环境变量
export PATH=/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/home/SXH/raspberry/tools-master/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin
pwd获得工作目录路径
cd /home/SXH/raspberry/tools-master/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin
echo $PATH 获得当前环境变量的值
/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
- 3.2配置永久环境变量
修改工作目录下的.bashrc 隐藏文件
vi /home/SXH/.bashrc
在文件最后一行加入下面的语句(前面是自己当前的环境变量,后面是你自己的工作目录路径,不要忘了加:)
export PATH=/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/home/SXH/raspberry/tools-master/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin
然后加载配置文件,马上生效配置
source /home/SXH/.bashrc
执行编译
首先检查下编译工具的版本号
arm-linux-gnueabihf-gcc -v
准备材料
1.pc
2.树莓派3b
我的history
1809 vi test.c
1810 gcc test.c -o pctest
1811 arm-linux-gnueabihf-gcc test.c -o armtest
1812 ls
1813 file pctest
1814 file armtest
1815 ./pctest
1816 ./armtest
1817 scp armtest pi@192.168.2.43:/home/pi
其中 scp armtest pi@192.168.2.43:/home/pi
语法为scp 命令是用于通过 SSH 协议安全地将文件复制到远程系统和从远程系统复制文件到本地的命令。使用 SSH 意味着它享有与 SSH 相同级别的数据加密,因此被认为是跨两个远程主机传输文件的安全方式。
scp [option] /path/to/source/file user@server-ip:/path/to/destination/directory
/path/to/source/file – 这是打算复制到远程主机的源文件。
user@server-IP: – 这是远程系统的用户名和 IP 地址。请注意 IP 地址后面加冒号。
/path/to/destination/directory – 这是文件将复制到的远程系统上的目标目录。
以下是scp命令常用的几个选项:
-C - 这会在复制过程中压缩文件或目录。
-P - 如果默认 SSH 端口不是 22,则使用此选项指定 SSH 端口。
-r - 此选项递归复制目录及其内容。
-p - 保留文件的访问和修改时间。
举例:scp demo1.c SXH@192.168.2.42:/home/SXH/raspberry
如图所示:
我们可以看到在Linux上执行可执行文件pctest可以打印结果,而执行可执行文件armtest反而出错bash: ./armtest: cannot execute binary file,在Linux编辑,在arm运行。
如图是arm crt显示终端:
带wiringPi库的交叉编译如何进行
条件:需要树莓派的专用库。
-
正常我们先要交叉编译wiringPi库,编译出的库适合树莓派,这时候交叉编译可执行程序的时候,链接库的格式也是正确的。
-
通过-I -L来指定
-
因为链接的库的格式不对,是宿主机的平台,出现以下错误
arm-linux-gnueabihf-gcc demo2.c -I /home/SXH/raspberry/WiringPi/wiringPi -lwiringPi
/home/SXH/raspberry/tools-master/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/…/lib/gcc/arm-linux-gnueabihf/4.8.3/…/…/…/…/arm-linux-gnueabihf/bin/ld: cannot find -lwiringPi
collect2: error: ld returned 1 exit status -
把树莓派的wringPI库拿上来用
-
arm-linux-gnueabihf-gcc demo1.c -I /home/SXH/raspberry/WiringPi/WiringPi/wiringPi -L . -lwiringPi
( 交叉编译工具连编译的格式 要编译的文件 -I指定头文件的路径(不然不认识,默认/usr/local/lib/路径) -L指定检索库的路径)
总结
交叉编译就是在一种平台上编译出能运行在体系结构不同的另一种平台上的程序,比如在PC平台(X86 CPU)上编译出能运行在以ARM为内核的CPU平台上的程序,编译得到的程序在X86 CPU平台上是不能运行的,必须放到ARM CPU平台上才能运行,虽然两个平台用的都是Linux系统。 交叉编译工具链是一个由编译器、连接器和解释器组成的综合开发环境,交叉编译工具链主要由binutils、gcc和glibc三个部分组成。有时出于减小 libc 库大小的考虑,也可以用别的 c 库来代替 glibc,例如 uClibc、dietlibc 和 newlib。