交叉编译⼊门及必要配置⽅法总结
交叉编译总结
本⽂是交叉编译⼊门及必要配置⽅法总结,⽬的为新⼿介绍如何进⼊交叉编译的世界,并附带两个重要列⼦:
第⼀个是使⽤cmake进⾏交叉编译
第⼆个是交叉编译Protobuf
交叉编译的⽬的是在⼀台架构A主机平台上编译另⼀种架构B⽬标平台的⼆进制⽂件或者库,交叉编译在⽬标系统平台(开发出来的应⽤程序序所运⾏的平台)难以或不容易编译时⾮常有⽤。主要体现在以下四个⽅⾯:
性能: ⽬标平台⽐主机平台性能差,许多专⽤的嵌⼊式硬件被设计为低成本和低功耗,导致编译慢
资源: 整个编译过程是⾮常消耗资源的,嵌⼊式系统往往没有⾜够的内存或磁盘空间
初始状态: 即便当⽬标平台性能资源都充⾜时,第⼀个在⽬标平台上运⾏的本地编译器总需要通过交叉编译获得
灵活兼容: 完整的Linux编译环境需要很多⽀持包,交叉编译使我们不需要花时间将各种⽀持包移植到⽬标板上
确定主机平台及⽬标平台
执⾏交叉编译最基本的就是明确⾃⼰主机平台和⽬标平台的架构。
1. 主机平台
主机平台⼀般都是⽇常使⽤的PC平台,为通⽤的Intel或者AMD的CPUx86架构,分32bit和64bit两种。当然也不排除使⽤其他平台架构作为主机平台。
2. ⽬标平台
⽬标平台就⽐较多,较为主流的有arm、mips、PowerPC等。每个平台下⾯都有各⾃的细分,主要还是分为32bit和64bit。
以arm为例,arm平台的指令集也在不断更新,⽬前存在⽐较多的是armv7和armv8两个平台。armv7为32位。armv8兼容32位和64位,有aarch32和aarch64。
mips平台在嵌⼊式领域也⽐较常见,龙芯就是采⽤mips架构进⾏开发的,为mips64。
获取交叉编译⼯具链
要执⾏交叉编译就需要有对应的交叉编译⼯具链,称之为⼯具链就是编译都是⼀整套⼯具,并不是单⼀的⼯具。⽐较熟知的就是gcc/g++编译器和ld连接器。
交叉编译⼯具链命名规则
通常交叉编译⼯具链命名规则为:arch-core-kernel-system。其中:
arch:⽬标平台架构,如上⽂提到的arm,mips等;
core:有两种种情况,第⼀是CPU Core,如Cortex A8;第⼆是指定⼯具链的供应商。如果没有特殊指定,则留空不填。这⼀组命名⽐较灵活,有以⼚家名称命名的,有以开发者命名的,也有以开发板命名的,或者直接是none或cross的;
kernel: ⽬标平台的OS,见过的有linux,uclinux,bare-metal(⽆OS);
system:嵌⼊式应⽤⼆进制接⼝(Embedded Application Binary Interface),交叉编译⼯具链所选择的库函数和⽬标映像的规范,如gnu,gnueabi等。其中gnu等价于glibc+oabi;gnueabi等价于glibc+eabi。若不指定,则也可以留空不填;
上述命名规则并不是统⼀的规范,使⽤的时候作为参考就⾏。我使⽤的交叉编译⼯具链名称为:gcc-linaro-7.5.0-2019.12-
x86_64_aarch64-linux-gnu。
获取交叉编译⼯具链两个途径:
1. 直接下载知名⼚家已经编译好的⼯具链。这些编译好的交叉编译⼯具链⼀般来说更加稳定,⽤的⼈多。
/downloads/
ftp.uk/pub/armlinux/toolchain/
www.denx.de/en/Software/WebHome
launchpad/gcc-arm-embedded
还有就是购买特定的嵌⼊式设备,对应⼚家会有对应的交叉编译⼯具链,如树莓派
2. ⾃⼰编译交叉编译⼯具链
这个⼀般不推荐,但是在⽆法下载到对应版本的⼯具链或者⼯具链某些c++特性不能⽀持等情况就需要⾃⼰根据⾃⼰的需要进⾏编译。
编译交叉编译⼯具链的⼯具:
crosstool-NG
Buildroot
Embedded Linux Development Kit (ELDK)
配置交叉编译环境
获取到对应的交叉编译⼯具链之后,需要在主机平台配置交叉编译环境。
⾸先解压交叉编译⼯具链。⼀般⼯具链的⽬录结构如下:
drwxr-xr-x 7  4096 Dec  4 19:53 aarch64-linux-gnu/
drwxr-xr-x 2  4096 Dec  4 19:55 bin/
-rw-r--r-- 1  11300 Dec  4 19:53 gcc-linaro-7.5.
drwxr-xr-x 3  4096 Dec  4 19:51 include/
drwxr-xr-x 3  4096 Dec  4 19:55 lib/
drwxr-xr-x 3  4096 Dec  4 19:37 libexec/
drwxr-xr-x 8  4096 Dec  4 19:51 share/
我们重点需要关注的是aarch64-linux-gnu和bin这两个⽬录。
【aarch64-linux-gnu】
【aarch64-linux-gnu】可能不同的交叉编译⼯具链名称不同,主要是平台架构不同,其他名称可能为【arm-linux-gnueabihf】。根据交叉编译⼯具链的名称不同⽽不同。
这个⽬录下的⽂件结构如下:
drwxr-xr-x 2  4096 Dec  4 19:54 bin/
drwxr-xr-x 3  4096 Dec  4 19:48 include/
drwxr-xr-x 3  4096 Dec  4 19:33 lib/
drwxr-xr-x 3  4096 Dec  4 19:55 lib64/
drwxr-xr-x 7  4096 Dec  4 19:53 libc/
在【aarch64-linux-gnu/bin】⽬录下有部分交叉编译⼯具,但是不全。其他⼏个⽬录都是必要的依赖库和头⽂件。我们重点关注的是【aarch64-linux-gnu/libc】,以后编译的其他依赖都需要安装在这个⽬录下的usr⽬录。因为这个⽬录类似于Linux系统的根⽬录,⽂件结构如下:
drwxr-xr-x  2  4096 Dec  4 19:41 etc/
drwxr-xr-x  3  4096 Dec  4 19:55 lib/
drwxr-xr-x  2  4096 Dec  4 19:41 sbin/
drwxr-xr-x 10  4096 Dec 25 17:37 usr/
drwxr-xr-x  3  4096 Dec  4 19:41 var/
在【aarch64-linux-gnu/libc/usr】⽬录下有lib和include⽬录。在交叉编译其他依赖的时候,指定的安装⽬录【–prefix】⼀般指定到这个⽬录中。
【bin】⽬录
在交叉编译⼯具链⽂件夹根⽬录下的【bin】⽂件夹是全部的交叉编译⼯具。⽐如gcc、g++编译器。可以在这个⽂件夹下执⾏以下编译器⼆进制⽂件,查看下版本,看看交叉编译器是否可⽤。
我们需要做的是将这个⽂件夹路径添加到环境变量PATH中:
export PATH=$PATH:/path/to/your/crosscompile-toolchain/bin
开始交叉编译
⾸先我们以⼀个传统的例⼦为例:
1. 第⼀步编写⼀个hello world程序。
#include <stdio.h>
void main()
{
printf("Hello world!!\n");
}
2. 我们就像使⽤主机平台gcc编译程序⼀样的过程编译hello world,这⾥以我的环境为例:
aarch64-linux-gnu-gcc helloworld.c -o helloworld
然后会⽣成⼀个可执⾏程序helloworld。你直接运⾏的话会报以下错误:
-bash: ./helloworld: cannot execute binary file: Exec format error
提⽰可执⾏程序格式错误。这是正常的。接着我们拷贝到⽬标平台上执⾏以下,检测是否正确输出“Hello world!!”。
这是基本的编译过程,稍微复杂点的也是⼀样,⽐如连接库⽂件的时候。只不过都需要使⽤交叉编译⼯具链来完成。
接下来我们讲两个复杂例⼦,第⼀个是使⽤cmake时的交叉编译⽅式,第⼆个是Protobuf的交叉编译。
使⽤cmake的交叉编译⽅式
在原项⽬使⽤的是cmake编译或者希望使⽤cmake进⾏交叉编译的时候。需要有⼏个变量进⾏定义。
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_SYSROOT /home/devel/rasp-pi-rootfs)
set(tools /home/devel/gcc-4.7-linaro-rpi-gnueabihf)
set(CMAKE_C_COMPILER ${tools}/bin/arm-linux-gnueabihf-gcc)
set(CMAKE_CXX_COMPILER ${tools}/bin/arm-linux-gnueabihf-g++)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
下⾯逐个介绍如下:
【CMAKE_SYSTEM_NAME】指定⽬标平台系统名称
【CMAKE_SYSTEM_PROCESSOR】指定⽬标平台CPU架构
【CMAKE_SYSROOT】这个很重要了,这个需要填写的是刚才交叉编译⼯具链根⽬录下的【aarch64-linux-gnu/libc】⽬录,这个⽬录当时介绍的时候说它类似主机平台Linux系统根⽬录,在这⾥指定的话,cmake交叉编译中会在这个⽬录下寻安装的其他⽬标平台依赖程序。
【tools】【CMAKE_C_COMPILER】【CMAKE_CXX_COMPILER】这三个变量主要是为了指定交叉编译⼯具的gcc、g++位置。【CMAKE_FIND_ROOT_PATH_MODE_PROGRAM】以及下⾯的【X_LIBRARY】【X_INCLUDE】【X_PACKAGE】三个变量都是指⽰CMake在执⾏find_X命令时的⾏为。有三个可选项,分别是NEVER,ONLY,BOTH。
NEVER表⽰不在你CMAKE_SYSROOT设置的⽬录下进⾏查;
ONLY表⽰只在这个路径下查;
BOTH表⽰先查这个路径,再查全局路径。
上⾯的变量有两种⽅式跟原先的CMakeLists结合:
1. 直接将上述⼏⾏变量定义代码放到原先CMakeLists的最开始处。有⼀点需要注意的是⼀定是在【project()】命令之前
2. 将上述⼏⾏变量定义代码放到单独的⽂件中,命名为ake,然后执⾏cmake命令的时候使⽤【-
DCMAKE_TOOLCHAIN_ake】
注:推荐第⼆种⽅式,但是第⼆种⽅式在进⾏交叉编译的时候,执⾏cmake容易出现⽆限循环检测,直到发送错误。
另外cmake在寻依赖的时候⼀般去刚才指定的CMAKE_SYSROOT这个⽬录中,通常是交叉编译⼯具链的【aarch64-linux-
gnu/libc】⽬录。因此交叉编译的依赖最好都安装在这个⽬录中。
Protobuf的交叉编译⽅式
这⾥额外介绍Protobuf的原因是,在交叉编译⼆进制的时候有时候需要对应主机平台的⼆进制协助,⽽且最好是版本⼀致的。Protobuf就是这样的,还有libmysqlclient。
所以⾸先先编译⼀个主机平台的Protobuf,然后再开始⽬标平台的编译。如果主机平台的Protobuf之前就有就可以直接⽤(版本最好⼀致)。
⽬标平台的编译过程:
1. Protobuf的压缩包⾥有⼀个【config.guess】⼯具,可以放到⽬标平台执⾏以下,会返回⽬标平台的架构、位数和系统。如:
aarch64-linux-gnu
2. 然后使⽤如下命令开始交叉编译,其中需要修改的是参数是【–host】【CC】【CXX】【–with-protoc】【–prefix】:
./configure --host=aarch64-linux-gnu CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-g++ --with-protoc=/usr/bin/protoc --prefix=/path/to/your/toolchain/ aarch64-linux-gnu/libc/usr
参数解释:
【–host】:就是⽤【config.guess】获取的;
【CC】【CXX】:交叉编译⼯具链gcc和g++名称;
【–with-protoc】:已经编译好的主机平台protoc⼆进制⽂件路径;
【–prefix】:交叉编译后安装⽬录。
总结
交叉编译的⼊门介绍就结束了。其实主要就⼏个步骤:
1. 确定主机平台和⽬标平台;
go语言安装教程2. 获取对应交叉编译⼯具链;
3. 开始交叉编译。
其实第三步还是⽐较复杂的,不同的软件各有不同,但是也是有⼀些共通的地⽅,就是:
1. 传⼊交叉编译⼯具的名称,如gcc和g++,其他还可能需要ld等。
2. 传⼊⽬标平台的架构、系统等详细信息,因为交叉编译的时候,软件本⾝⽆法去检测平台的特性,需要明确传⼊。
3. 安装⽬录(可选)
主要是这些参数的传⼊⽅式不同,需要具体⽤到的时候具体去查⼀下。
其他语⾔
上⽂整体上是介绍C/C++ 为主要⽅向的交叉编译过程。其实还有很多其他语⾔也是需要交叉编译的,⽐如go,rust。基本的理念是⼀致的,就是在编译的时候告知⽬标平台架构。
go语⾔的交叉编译⽐较简单,主要涉及GOOS和GOARCH两个参数,GOOS设置⽬标平台操作系统,GOARCH设置⽬标平台架构:CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build
这⾥不展开讲了,⼤家遇到的话可以针对性的查。
以上就是全部内容了,觉得有⽤就点赞啊

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。