交叉编译工具、交叉编译链的安装使用
文章目录
- 交叉编译工具、交叉编译链的安装使用
- 一、交叉编译
- 1.1什么是交叉编译
- 1.2为什么要交叉编译
- 1.3宿主机和目标机
- 二、搭建交叉编译工作环境
- 2.1安装工具链
- 2.2配置环境变量
- ● 配置临时环境变量
- ● 配置永久环境变量
- 三、交叉编译宿主机和目标机
- 3.1交叉编译出可执行文件
- 3.2宿主机编译生成的可执行文件下载到目标机(开发板)
- 四、交叉编译带wiringPi库的程序
- 五、软链接和硬链接
- 5.1 软链接
- 5.2 硬链接
- 六、带有wiringPi库进行交叉编译
- 附录
- 检索目录下所有带有gcc的命令
- 注意:踩坑1
- 最新补充:最新版的软链接指向变了
- 踩坑2:
一、交叉编译
1.1什么是交叉编译
(1)编译:是在一个平台上生成在该平台上的可执行代码
(2)交叉编译:是在一个平台上生成另一个平台上的可执行代码。
玩过51单片机的应该都知道,我们是在Windows上面编译,生成.hex文件,烧录到单片机上面,在51单片机上面运行,这其实就是最早的交叉编译。那为什么不在单片机上面运行编译,要在Windows上面,因为单片机安装不下配置环境,内存很小。
现在我们要做的就是不在树莓派什么进行编译了,在ubantu(本篇文章在虚拟机下\宿主机,进行编译代码,然后跑到树莓派上面运行)
● 我们在windows上面编写C51代码,并编译成可执行代码,如xx.hex,是在c51上面运行,不是在windows上面运行;
● 我们在ubuntu上面编写树莓派的代码,并编译成可执行代码,如a.out,是在树莓派上面运行,不是在ubuntu linux上面运行
1.2为什么要交叉编译
- 平台上不允许或不能够安装我们所需要的编译器比如C51;
- 因为目的平台上的资源贫乏,无法运行我们所需要编译器;
- 树莓派作为一款强大的开发板,同样也需要用到交叉编译。树莓派有时因为目的平台还没有建立,暂无操作系统,所以根本不能运行编译器。
- 操作系统也是代码,也要编译!
1.3宿主机和目标机
平台运行需要两样至少东西:bootloader(启动引导代码)以及操作系统核心。
● 宿主机(host) :编辑和编译程序的平台,一般是基于X86的PC机,通常也被称为主机(电脑X86)。
● 目标机(target):用户开发的系统,通常都是非X86平台。host编译得到的可执行代码在target上运行(树莓派ARM)。
二、搭建交叉编译工作环境
2.1安装工具链
树莓派官方下载网址:https://github.com/raspberrypi/
交叉工具链下载链接:https://github.com/raspberrypi/tools.git
1.下载工具链
两种方法下载工具链:
方法一:直接去官网下载,具体步骤看下图
方法二:输入命令 git clone https://github.com/raspberrypi/tools.git
方法一:
搜索tools,找到tools工具之后下载完成之后导入到ubantu系统之中即可
下载完之后,自己找一个文件夹,解压出来。
(如果是使用git命令下载下来的,默认就是一个tools文件,无需再解压)
unzip tools-master.zip
2.进入文件目录中,依次进入如下目录直到bin目录(64位计算机就如下选择)
进入bin目录里,找到 arm-linux-gnueabihf-gcc
cd tools-master/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin
3.ls -l 查看bin目录底下各文件权限,arm-linux-gnueabihf-gcc作为软连接,实际用到的可执行程序是它后面的arm-linux-gnueabihf-gcc-4.8.3
ls -l
2.2配置环境变量
临时:只在当前页面有效,风险性高,离开当前页面又需要重新配置;
永久:任何页面或路径下都有效,安全可靠
(1)Linux环境变量的作用和配置方法其实和Windows的环境变量一样,不一样的只有界面而已;
(2)在上面操作交叉编译工具链可以看到,要使用工具链就必须进入层层目录,繁琐且麻烦,降低开发效率;
(3)配置环境变量就能很好的解决这一问题;
● 配置临时环境变量
(1)echo $PATH :显示当前的环境变量;
(2)pwd :找到tools-master/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin,并复制;
(3)export PATH=$PATH:自定义路径
(4)arm-linux-gnueabihf-gcc -v :检查是否找到,如果不配置的话只能在工具链的目录找到,配置完之后在任何目录下都可找到
export PATH=$PATH:/home/a/tools/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin
临时变量重启之后就没了。
● 配置永久环境变量
(1)修改工作目录下的.bashrc 隐藏文件,配置命令终端的vi /home/你的用户名/.bashrc 打开后在文本最后一行加入以下内容:
export PATH=$PATH:你的工具链存放的路径
(2)source /home/用户名/.bashrc 加载配置文件,马上生效配置。
export PATH=$PATH:/home/a/tools/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin
指令arm-linux-gnueabihf-gcc -v 检查交叉工具链是否是4.8.3版本
arm-linux-gnueabihf-gcc -v
三、交叉编译宿主机和目标机
3.1交叉编译出可执行文件
检查交叉编译工具链:arm-linux-gnueabihf-gcc -v
在Ubuntu上编写一个简单的程序:程序的文件名为:hello.c
#include <stdio.h>
int main()
{
printf("hello,world");
return 0;
}
我们分别查看一下常规编译与交叉编译生成的可执行程序的区别。
常规编译(使用gcc编译器编译):gcc hello.c -o a
交叉编译(使用树莓派的交叉编译器arm-linux-gnueabihf-gcc编译):arm-linux-gnueabihf-gcc hello.c -o b
用file命令查看可程序a和b的属性:
● a在x86-64位计算机上面运行(宿主机-电脑),无法在ARM平台上运行
● b在ARM平台上运行(目标机-树莓派),无法在X86平台上运行(这是一个二进制文件)
3.2宿主机编译生成的可执行文件下载到目标机(开发板)
使用scp
命令,可通过网络IP实现文件的互传。
scp b pi@192.168.x.xxx:/home/pi
指令 文件名 开发板用户名@开发板地址:开发板的绝对路径
此方法也可以将目标机文件传至宿主机
四、交叉编译带wiringPi库的程序
说明:在树莓派中编写带wiringPi库的程序时(比如IO口),直接在程序中加头文件#include <wiringPi.h>
,在编译的时候-l链接wiringPi库即可,因为树莓派自带有wiringPi库,在/usr/lib目录下。在Ubuntu中或者宿主机中没有自带wiringPi的库,那么很明显无法顺利完成带wiringPi库程序的交叉编译。
git clone git://git.drogon.net/wiringPi
出现的问题:
1.现在写一个wiringPi.h的程序。
//demo.c #include <stdio.h> #include <wiringPi.h> #include <stdlib.h> #define PIN 7 //#define宏定义控制引脚为GPIO.7 int main() { int cmd; if(wiringPiSetup() == -1){ //初始化wiringPi库 printf("init error\n"); return -1; } pinMode(PIN,OUTPUT); //设置为输出io口 digitalWrite(PIN,HIGH);//默认断电状态 while(1){ printf("please input 0 or 1:0-close,1-open\n");//输入0或1,0关闭,1打开 scanf("%d",&cmd); if(cmd == 1){ digitalWrite(PIN,LOW);//输入的是1,就给低电平,供电 }else if(cmd == 0){ digitalWrite(PIN,HIGH);//输入的是0,就给高电平,断电 }else{ printf("OUTPUT error\n");//如果输入的不是1或0 digitalWrite(PIN,HIGH);//就自动断电 exit(-1); //并退出 } } return 0; }
2.交叉编译这个文件,我们发现找不到这个头文件
arm-linux-gnueabihf-gcc demo.c
3.OK好,那我去找到这个头文件,直接链接到目录下就可以了吧
我们找到他在目录:
/home/a/raspberrypi/WiringPi/wiringPi
下。4.我们之间链接到头文件目录下,发现报错又变了
arm-linux-gnueabihf-gcc demo.c -I /home/a/raspberrypi/WiringPi/wiringPi
5.这个报错其实是跟树莓派一样的,如果不链接到库
//这个代码如果在树莓派底下运行同样会报相同的错误,gcc demo.c -lwiringPi 就可以解决 gcc demo.c
6.那我们再试一下,发现报错又变了,这个**-l**是去哪里链接了呢?他找不到这个库
arm-linux-gnueabihf-gcc demo.c -I /home/a/raspberrypi/WiringPi/wiringPi -lwiringPi
7.-l,一般都会跑到**/usr/local/lib**下面去链接库
ls /usr/local/lib
8.接下来我们怎么做?直接去树莓派主机上面,把他的wiringpi库拿来用。
● 将树莓派中自带的wiringPi库下载到Ubuntu或宿主机中
① cd /usr/lib
:树莓派wiringPi库的文件路径;
② ls -l |grep wiringPi
:过滤出只有wiringPi库的相关文件
->表示软链接的意思
③ 将软链接libwiringPi.so指向的libwiringPi.so.2.52下载到Ubuntu或宿主机中:
scp libwiringPi.so.2.52 a@192.168.x.x:/home/a/raspberrypi
指令 需要拷贝的文件名 目标用户名 ip 拷贝的目标路径
scp libwiringPi.so.2.52 a@192.168.237.130:/home/a/raspberrypi
注意:这里要拷贝软连接指向的文件,如果直接拷贝软链接到Ubuntu,Ubuntu里并不会生成我们要的软链接。
需要我们自己生成软链接
五、软链接和硬链接
5.1 软链接
● 软链接文件有类似于Windows的快捷方式;
● 在符号连接中,文件实际上是一个文本文件,其中包含的有另一文件的位置信息;
● 在选定的位置上生成一个文件的镜像,不会占用磁盘空间。
生成方法:
还记得我们之前学的生成动态库的过程吗,就是以.so结尾的
ln -s libwiringPi.so.2.52 libwiringPi.so
指令 参数 要被链接的文件 要生成的软链接文件名字
5.2 硬链接
● 在选定的位置上生成一个和源文件大小相同的文件;
● 硬链接通过索引节点来进行链接;在Linux的文件系统中,保存在磁盘分区中的文件不管是什么类型都给它分配一个编号,称为索引节点号(Inode Index)。在Linux中,多个文件名指向同一索引节点是存在的。一般这种连接就是硬连接
● 允许一个文件拥有多个有效路径名,这样用户就可以建立硬链接到重要文件,以防止“误删”的功能。因为对应该目录的索引节点有一个以上的连接。只删除一个连接并不影响索引节点本身和其它的连接,只有当最后一个连接被删除后,文件的数据块及目录的连接才会被释放,也就是说,文件真正删除的条件是与之相关的所有硬连接文件均被删除。
生成方法:
ln libwiringPi.so.2.50 libwiringPi.so
没有参数 -s
无论是软链接还是硬链接,文件都保持同步变化。
六、带有wiringPi库进行交叉编译
编译时通过-I -L来指定相关库的路径
● -I (注意是大写的i) 给gcc添加自定义的头文件的路径
● -L 给gcc添加额外的搜索库的路径
**说明:**demo.c是带有wiringPi.h
的一个文件,是需要连接wiringPi库使用的程序;-I的wiringPi库的头文件是自行网上下载的wiringPi库的包;-L的wiringPi库是树莓拷贝到Ubuntu中的 。
arm-linux-gnueabihf-gcc demo.c -I /home/a/raspberrypi/WiringPi/wiringPi -L. -lwiringPi -o test
交叉编译工具链 树莓关于IO口文件 -I wiringPi库头文件路径 -L .在当前目录下 wiringPi库
附录
检索目录下所有带有gcc的命令
grep gcc -nir
注意:踩坑1
查看一下工具链下的readme文档,安装了工具链
sudo apt-get install gcc-arm-linux-gnueabihf -y
sudo apt-get install gcc-arm-linux-gnueabihf -y
,安装完之后已经无需再配置环境变量了,配置环境变量这一部分可以直接跳过,如果无法使用再跳回来配置。
最新补充:最新版的软链接指向变了
变成了9.4.0的版本,这个时候,交叉编译是可以通过的,但是在后面进行wiringPi库链接时,就会出现一大堆问题,报错代码如下。解决办法就是将下载的交叉工具编译链全部卸载,严格按照上面的步骤进行下来。
a@ubuntu:~/raspberrypi$ arm-linux-gnueabihf-gcc demo.c -I /home/a/raspberrypi/WiringPi/wiringPi -L. -lwiringPi -o test
/usr/lib/gcc-cross/arm-linux-gnueabihf/9/../../../../arm-linux-gnueabihf/bin/ld: warning: libcrypt.so.1, needed by ./libwiringPi.so, not found (try using -rpath or -rpath-link)
/usr/lib/gcc-cross/arm-linux-gnueabihf/9/../../../../arm-linux-gnueabihf/bin/ld: ./libwiringPi.so: undefined reference to `crypt@GLIBC_2.4'
collect2: error: ld returned 1 exit status
踩坑2:
报错:/libwiringPi.so: undefined reference to fcntl@GLlBC 2.28’交叉编译wiringPi库遇到了这个错误
因为要链接到动态库:-lwiringPi。
但是因为宿主机与目标机的glibc的版本不一样,你可以理解为gcc的编译版本不一致。
宿主机x86的为:2.31版本,而目标机ARM上面的为2.28版本
这个错误通常是由于交叉编译时使用的 gjlibc 版本与目标系统上的 glibc 版本不兼容导致的。GLIBC 2.28 是在较新的 glibc 版本中引入的函数,如果您的目标系统上的 glibc 版本低于 2.28,则会出现该错误。
解决这个问题的一种方法是更新目标系统上的 glibc 版本,以便与编译时使用的版本匹配。如果您无法更新目标系统上的 glibc 版本,可以尝试降低编译时使用的 glibc 版本。
博主做了哪些尝试呢?
第一个:在宿主机上面自己做一个wiringPi动态库,之前有讲过如何制作,找到官方的wiringPi库,进行制作。
但是后面由于不知道具体包含哪些源文件,所以制作出来的会报一些错误,用不了。
在这里我们要注意一下,-lwiringPi,链接的这个库libwiringPi.so必须是ARM架构的,也就是制作动态库时要用交叉编译arm-linux-gnueabihf-gcc,来编译这些文件
第二个:把官方wiringPi下载下来之后可以自己配置,但是他们默认是使用的gcc工具进行编译,编译完成之后这个.so文件是x86架构的,所以导致与目标机的架构不匹配,也无法运行。
解决办法:输入命令,我们发现好多都是gcc来编译的,在宿主机上面gcc编译出来的东西肯定是X86架构的,想要编译出来ARM架构的就需要arm-linux-gnueabihf-gcc来进行交叉编译。那么我们将gcc换成arm-linux-gnueabihf-gcc不就可以了吗
grep gcc -nir
你可以使用 sed
命令或文本编辑器来批量替换 Makefile
中的 gcc
。以下是使用 sed
命令的示例
find . -name 'Makefile' -exec sed -i 's/gcc/arm-linux-gnueabihf-gcc/g' {} +
这个命令会在当前目录及其子目录下查找所有名为 Makefile
的文件,并将其中的 gcc
替换为 arm-linux-gnueabihf-gcc
。
2.确保替换操作成功执行。你可以使用 grep
命令确认 gcc
是否已被替换:
grep -r 'arm-linux-gnueabihf-gcc' .
3.将所有gcc的换成arm-linux-gnueabihf-gcc之后,我们就可以回到wiringPi库底下重新进行安装。
./bulid
4.这时我们再查看这个.so文件时,已经变成ARM架构的了
5.交叉编译运行wiringPi库
已经可以编译成功了,比较纳闷的就是,指定在/usr/local/lib目录下去找wiringPi库找不到,我把他复制到当前目录下,并创建好软链接之后便可以生成可执行文件且可以运行了。不过好歹也是能够交叉编译wiringPi库了。