多架构与交叉编译
交叉编译
我们日常使用的系统基本都是基于 x86 架构的,但是在嵌入式设备和 IOT 漏洞分析中,通常需要接触其他的架构,如:ARM、MIPS 等,而 x86 架构下编译的二进制程序是无法在 ARM、MIPS 架构下运行的
如果我们想要在自己的 x86 架构电脑上编译二进制程序,然后在嵌入式设备上运行,就需要配置交叉编译环境,即:在 x86 架构的系统上编译 ARM、MIPS 等架构的二进制程序
查看系统架构
学习多架构,首先当然需要了解一下自己的机器是什么架构
- 查看内核版本
# 三选一即可
cat /proc/version
uname -a
uname -r- 查看 Linux 版本信息
# 二选一即可
lsb_release -a
cat /etc/issue- 查看系统位数
# 二选一即可
getconf LONG_BIT
file /bin/ls- 查看系统架构
# 三选一即可
arch
dpkg --print-architecture
file /lib/systemd/systemdARM 架构
ARM 架构在移动设备(如:智能手机、平板电脑等)和嵌入式系统中非常常见,而 x86 架构主要用于桌面和服务器领域
各种 ARM 架构名称的区别
如果刚开始接触 ARM 架构,大概会被它的各种名称弄混吧,例如:ARM、ARM64、AArch32、AArch64、ARMv7、ARMv8 等等
就像 x86 架构的 x86、x86_64、i386、amd64 等等,其实有些名称是同一个东西,只是人们的使用习惯问题
官方认定的 32 位和 64 位 ARM 架构的名称分别是 AArch32 和 AArch64(这里的 AArch 其实就是 ARM Architecture 的缩写)
实际符合 ARM 的 CPU ISA 的指令规范被命名为 ARMvX(其中 X 是规范版本的代表数字),目前已经有九个主要的规范版本:
ARMv1到ARMv7是适用于 32 位 ARM CPU 的规范ARMv8和ARMv9是适用于 64 位 ARM CPU 的规范
通常来说,在 Linux 中 arm 指代 32 位,而 aarch64 指代 64 位
你可能会觉得困惑,为什么在
AArch64正式被 ARM 认定为 64 位 ARM 架构后,有些人仍然称其为arm64原因主要有两点:
arm64这个名称在 ARM 决定采用AArch64之前就已经广为人知了,甚至 ARM 的一些官方文档也将 64 位的 ARM 架构称为arm64- Linus Torvalds 对
AArch64这个名称表示不满,因此 Linux 的代码库主要将AArch64称为arm64,然而,当你在系统中运行uname -m时,输出的仍然是aarch64因此,对于 32 位 ARM CPU,你应该寻找
AArch32这个字符串,但有时也可能是arm或armv7类似的,对于 64 位 ARM CPU,你应该找
AArch64这个字符串,但有时也可能会是arm64、ARMv8或ARMv9
交叉编译环境搭建
注意:
arm-linux-gnueabihf-gcc是 32 位的 ARM 编译器aarch64-linux-gnu-gcc是 64 位的 ARM 编译器
这里以 32 位的 ARM 编译器为例,64 位的 ARM 编译器将 arm 改为 arrch64 即可(同时要注意 AArch64 不再是 gnueabihf 而是 gnu)
由于我本机是 64 位的 x86 架构 Kali Linux,首先需要安装 32 位库:
sudo dpkg --add-architecture i386
sudo apt update
sudo apt install libncurses5-dev lib32z1
sudo apt install libc6:i386 libstdc++6:i386apt安装
这种安装方式的优点是简单,但缺点是受版本的限制,如果需要编译特定的版本,则建议手动编译安装
搜索合适的版本:
# 这里主要搜索 arm 架构,可以按照需要修改
apt-cache search arm-linux | grep -E 'gcc|g\+\+'图中可以看到主要有 gnueabi 和 gnueabihf 两个版本,它们的区别在于浮点运算的性能:
gnueabihf:使用硬浮点 ABI,要求目标设备具备硬件浮点单元,能够提高浮点运算的性能gnueabi:使用软浮点 ABI,不要求目标设备具备硬件浮点单元,通过软件模拟浮点操作,性能较低
其他关于
abi与eabi、gnueabi与gnueabihf的详细区别见:ARM工具链选择参考 - ArnoldLu - 博客园
安装 arm-linux-gcc 和 arm-linux-g++:
# 也可以指定其他版本,参照上图,这里演示默认安装
sudo apt install gcc-arm-linux-gnueabihf
sudo apt install g++-arm-linux-gnueabihf测试安装:
arm-linux-gnueabihf-gcc -v
arm-linux-gnueabihf-g++ -v卸载:
sudo apt remove gcc-arm-linux-gnueabihf
sudo apt remove g++-arm-linux-gnueabihf- 手动编译安装
因为我本机是 x86 架构,通过编译安装就需要用到交叉编译器
交叉编译器有很多,这里以 Linaro 出品的交叉编译器为例(这只是支持交叉编译的 GCC,能用就行,至于哪一家的无所谓)
首先下载 Linaro arm-linux-gnueabihf 架构的 GCC:Linaro GCC v7.5
这里的位数根据本机来确定,我这里是 x86_64 的 Kali Linux,因此选择下面那个
其他版本的 Linaro GCC 下载:Linaro Releases(目前截止到 2019 年 GCC v7.5)
新版本的 Linaro GCC 下载:Linaro Snapshots,官网链接:Downloads | Linaro
注意:
这个 Linaro GCC 的版本不是越新越好,不同的开发板的根文件系统的版本是不同的,高版本的编译器编译的程序在低版本的根文件系统中不能运行
如果出现不能运行的情况,要么降低交叉编译器的版本,要么升级开发板的根文件系统
创建一个文件夹用于存放 ARM 架构的交叉编译工具:
wget https://releases.linaro.org/components/toolchain/binaries/latest-7/arm-linux-gnueabihf/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz
sudo tar -vxf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz
sudo mkdir /usr/local/arm
sudo mv gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf /usr/local/arm为了在终端直接使用交叉编译工具,接着添加环境变量:
sudo vim /etc/profile
# 在文件的最后,加入下面两个----之间的内容
-----------------------------------------------------------------
export PATH=$PATH:/usr/local/arm/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/arm/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/lib
-----------------------------------------------------------------验证环境变量:
source /etc/profile # 使环境变量生效,如果重启机器,可能需要再执行一次
arm-linux-gnueabihf-gcc -v到这里就配置成功了
ARM 交叉编译
接下来测试一下 ARM 的交叉编译,编写一个简单的 C 程序:
#include<stdio.h>
int main()
{
printf("hello ARM !!!\n");
return 0;
}然后使用 arm-linux-gnueabihf-gcc 编译并查看二进制文件:
arm-linux-gnueabihf-gcc test.c -o my_arm_file
file my_arm_file如果没有出现报错,说明我们的环境是没有问题的
可以看到 my_arm_file 是一个 32 位的 ARM 架构的二进制文件(小端序):
但是由于我们本机是 x86 架构,因此是无法运行该程序的:
这里显示我们缺少 /lib/ld-linux-armhf.so.3 文件
为了避免出现
lib库的问题,建议使用静态编译,即加上参数-static:arm-linux-gnueabihf-gcc test.c -o my_arm_file -static这样就不会再报这种缺少
lib文件的错误了
安装相关库:
sudo apt install libc6-armhf-cross成功后会有 /usr/arm-linux-gnueabihf 文件夹,这里面有我们缺少的文件:
想要运行该 ARM 架构的程序,我们需要先安装 QEMU
详见本站的《IOT环境搭建与固件分析》一文的《安装 QEMU》部分
使用 QEMU 的用户级工具 qemu-arm-static 来执行 ARM 架构的二进制程序:
# 使用 -L 指定运行的 lib 环境
qemu-arm-static -L /usr/arm-linux-gnueabihf/ ./my_arm_file可以看到执行成功
MIPS 架构
MIPS 架构广泛被使用在许多电子产品、网络设备、个人娱乐设备与商业设备上,是一种采取精简指令集(RISC)的处理器架构
各种 MIPS 架构名称的区别
相比于 ARM 架构,MIPS 架构的名称就少多了
MIPS 的 32 位主要有 mips 和 mipsel 两种,它们的区别在于:
mips是大端序的mipsel是小端序的
我们日常使用的 x86 架构的 Windows 系统通常也是小端序的
另外,mips 和 mipsel 对应的 64 位分别为 mips64 和 mips64el,同样是对应大端序和小端序的关系
通常来说,在 Linux 中 mips 指代 32 位大端序,而 mipsel 指代 32 位小端序;mips64 指代 64 位大端序,而 mips64el 指代 64 位小端序
交叉编译环境搭建
注意:
与 ARM 和 AArch64 不同,MIPS 的交叉编译器可以直接通过
-mabi=32和-mabi=64参数来指定生成 32 位或 64 位的二进制程序(编译 64 位需要安装带multilib的版本)但是,**
mips编译的二进制程序是大端序,mipsel编译的是小端序**
这里以大端序的 MIPS 编译器为例,小端序的 MIPS 编译器将 mips 改为 mipsel 即可,其他操作完全一致
由于我本机是 64 位的 x86 架构 Kali Linux,首先需要安装 32 位库:
sudo dpkg --add-architecture i386
sudo apt update
sudo apt install libncurses5-dev lib32z1
sudo apt install libc6:i386 libstdc++6:i386apt安装
与 ARM 架构的交叉编译环境搭建类似,这种安装方式的优点是简单,但缺点是受版本的限制,如果需要编译特定的版本,则建议手动编译安装
搜索合适的版本:
# 这里主要搜索 mips 架构,可以按照需要修改
apt-cache search mips-linux | grep -E 'gcc|g\+\+'图中可以看到主要有带 multilib 和不带 multilib 的两个版本,带 multilib 的版本适用于编译支持多种不同的目标架构和 ABI 的程序的场景,比如想要编译 64 位的程序就需要安装带 multilib 的版本
安装 mips-linux-gcc 和 mips-linux-g++:
# 也可以指定其他版本,参照上图,这里演示默认安装
# 安装 32 位编译器
sudo apt install gcc-mips-linux-gnu
sudo apt install g++-mips-linux-gnu
# 安装 64 位编译器
sudo apt install gcc-multilib-mips-linux-gnu
sudo apt install g++-multilib-mips-linux-gnu安装相关依赖:
sudo apt install linux-libc-dev-mips-cross libc6-mips-cross libc6-dev-mips-cross binutils-mips-linux-gnu测试安装:
mips-linux-gnu-gcc -v
mips-linux-gnu-g++ -v卸载:
sudo apt remove gcc-mips-linux-gnu
sudo apt remove g++-mips-linux-gnuMIPS 交叉编译
接下来测试一下 MIPS 的交叉编译,编写一个简单的 C 程序:
#include<stdio.h>
int main()
{
printf("hello MIPS !!!\n");
return 0;
}然后使用 mips-linux-gnu-gcc 编译并查看二进制文件:
# 编译 32 位二进制程序
# 也可以不加 -mabi=32 参数,默认编译为 32 位程序
mips-linux-gnu-gcc -mabi=32 test.c -o my_mips32_file
file my_mips32_file如果没有出现报错,说明我们的环境是没有问题的
可以看到 my_mips32_file 是一个 32 位的 MIPS 架构的二进制文件(大端序):
# 编译 64 位二进制程序
mips-linux-gnu-gcc -mabi=64 test.c -o my_mips64_file
file my_mips64_file在编译 64 位程序时,如果没有安装带 multilib 的 mips-linux-gnu-gcc 版本,会出现如下报错:
In file included from /usr/mips-linux-gnu/include/features.h:514,
from /usr/mips-linux-gnu/include/bits/libc-header-start.h:33,
from /usr/mips-linux-gnu/include/stdio.h:27,
from test.c:1:
/usr/mips-linux-gnu/include/gnu/stubs.h:35:11: fatal error: gnu/stubs-n64_hard.h: 没有那个文件或目录
35 | # include <gnu/stubs-n64_hard.h>
| ^~~~~~~~~~~~~~~~~~~~~~
compilation terminated.安装了带 multilib 的 mips-linux-gnu-gcc 版本后,可以看到 my_mips64_file 是一个 64 位的 MIPS 架构的二进制文件(大端序):
但是由于我们本机是 x86 架构,因此是无法运行该程序的:
这里显示我们缺少 /lib64/ld.so.1 文件
为了避免出现
lib库的问题,建议使用静态编译,即加上参数-static:mips-linux-gnu-gcc -mabi=32 test.c -o my_mips32_file -static mips-linux-gnu-gcc -mabi=64 test.c -o my_mips64_file -static这样就不会再报这种缺少
lib文件的错误了
安装相关库:
sudo apt install libc6-mips-cross成功后会有 /usr/mips-linux-gnu 文件夹,这里面有我们缺少的文件:
想要运行该 MIPS 架构的程序,我们需要先安装 QEMU
详见本站的《IOT环境搭建与固件分析》一文的《安装 QEMU》部分
使用 QEMU 的用户级工具 qemu-mips-static 和 qemu-mips64-static 来执行 MIPS 架构的二进制程序:
# 使用 -L 指定运行的 lib 环境
qemu-mips-static -L /usr/mips-linux-gnu/ ./my_mips32_file
# 使用 -L 指定运行的 lib 环境
qemu-mips64-static -L /usr/mips-linux-gnu/ ./my_mips64_file可以看到执行成功































