凌云的博客

行胜于言

如何在 x86 Ubuntu 系统下交叉编译 ARM 架构下运行的软件

分类:Linux| 发布时间:2021-03-24 15:37:00


前言

最近需要将原本运行于 x86_64 Linux 下的软件移植到 ARM 架构下,由于开发和自测主要还是在 x86_64 下进行, 因此决定使用交叉编译的方式生成 ARM 架构下的可执行文件。

本文主要记录了在 x86_64 Linux 下进行交叉编译所需的环境和要点。

环境如下:

  • 系统:Ubuntu 16.04/18.04 [WSL也可以]

软件需要依赖的开源库有:

项目中需要进行移植的代码主要为 C/C++ 代码,另外还有部分组件是使用 golang 编写而成,也需要针对 ARM 架构进行重新编译

交叉编译环境搭建

假设系统已经搭建好的 x86_64 的编译环境,在此基础上需要进行 aarch64 架构的编译只需要安装如下软件:

$ sudo apt install g++-aarch64-linux-gnu

编译依赖库

protobuf

protobuf 官方提供了 CMakeLists.txt 进行项目构建,这里主要介绍如何通过 CMakeLists.txt 进行 protobuf 的交叉编译。

假设我们使用如下命令编译 x86_64 版本的 protobuf

$ cmake -Dprotobuf_BUILD_TESTS=OFF -Dprotobuf_WITH_ZLIB=OFF -DBUILD_SHARED_LIBS=ON protobuf/cmake

为了进行交叉编译需要通过变量 CMAKE_TOOLCHAIN_FILE 指定工具链的位置:

$ cmake -Dprotobuf_BUILD_TESTS=OFF -Dprotobuf_WITH_ZLIB=OFF -DBUILD_SHARED_LIBS=ON -DCMAKE_TOOLCHAIN_FILE=aarch64.cmake protobuf/cmake

aarch64.cmake 的内容为:

SET (CMAKE_SYSTEM_PROCESSOR aarch64)
SET (CMAKE_C_COMPILER /usr/bin/aarch64-linux-gnu-gcc)
SET (CMAKE_CXX_COMPILER /usr/bin/aarch64-linux-gnu-g++)

CMAKE_C_COMPILER 和 CMAKE_CXX_COMPILER 的值可以根据实际情况进行修改

OpenSSL

我们用如下命令编译 x86_64 版本的 OpenSSL:

$ perl openssl/Configure linux-x86_64
$ make -j8

交叉编译 OpenSSL 需要修改为:

$ CC=/usr/bin/aarch64-linux-gnu-gcc MACHINE=aarch64 perl openssl/Configure linux-aarch64
$ make -j8

Boost

我们用如下命令编译 x86_64 版本的 Boost:

./bootstrap.sh
./b2 --with-chrono --with-date_time --with-filesystem --with-locale --with-regex --with-serialization --with-system variant=release

交叉编译 Boost 需要修改为:

./bootstrap.sh
echo "using gcc : $ARCH : /usr/bin/aarch64-linux-gnu-g++ ;" > user-config.jam
BOOST_BUILD_PATH=`pwd` ./b2 --with-chrono --with-date_time --with-filesystem --with-locale --with-regex --with-serialization --with-system variant=release install

为了能让 Boost 的编译系统识别到我们需要使用自定义的编译器,我们需要生成 user-config.jam 文件,在这个文件指定 g++ 的位置。

同时还要通过环境变量 BOOST_BUILD_PATH 让 Boost 的编译系统找到我们的 user-config.jam 文件。

xxHash

我们用如下命令编译 x86_64 版本的 xxHash:

$ make -j8

交叉编译 Boost 需要修改为:

CC=/usr/bin/aarch64-linux-gnu-gcc CXX=/usr/bin/aarch64-linux-gnu-g++ make -j8

简单来说就是通过 CC 和 CXX 环境变量指定交叉编译其的路径即可。

lz4 和 zstd 的交叉编译都是类似的,这里不再赘述。

rsync

我们用如下命令编译 x86_64 版本的 rsync

./configure CPPFLAGS="-I$XXHASH_ROOT/include -DXXH_STATIC_LINKING_ONLY -I$LZ4_ROOT/include -I$ZSTD_ROOT/include -I$OPENSSL_ROOT/include" LIBS="$XXHASH_LIBRARIES $LZ4_LIBRARIES $ZSTD_LIBRARIES $OPENSSL_CRYPTO_LIBRARIES"

交叉编译的 rsync 需要修改为:

./configure --host=aarch64-pc-linux-gnu CPPFLAGS="-I$XXHASH_ROOT/include -DXXH_STATIC_LINKING_ONLY -I$LZ4_ROOT/include -I$ZSTD_ROOT/include -I$OPENSSL_ROOT/include" LIBS="$XXHASH_LIBRARIES $LZ4_LIBRARIES $ZSTD_LIBRARIES $OPENSSL_CRYPTO_LIBRARIES" --disable-simd

交叉编译 rsync 主要通过 --host 参数指定要编译出来的 rsync 的架构,如果你的交叉编译器不在 $PATH 目录下, 需要使用 CC 环境变量指定。

由于 v3.2.3 的编译脚本在进行交叉编译的情况下有 BUG 因此需要显式指定 --disable-simd 参数,目录官方最新的 master 分支是已经修复这个问题了。

有兴趣的可以 点击 查看详情。

编译自有代码

C++ 部分

我们的 C++ 代码部分是通过 CMakeList.txt 进行构建的,交叉编译自有 C++ 代码与 protobuf 类似,这里不再赘述。

golang 部分

golang 进行交叉编译非常简单,只需要通过环境变量 GOARCH 和 GOOS 指定要编译的环境和架构即可。 在这里我们只需要指定 GOARCH 为 arm64 即可。

另外可以通过命令:

$ go tool dist list

查看所有合法的 GOOS 和 GOARCH 组合。

小结

本文主要描述了如何在 x86_64 的 Linux 系统交叉编译出 arm64 Linux 运行的 C++/C 和 golang 程序。

根据构建系统主要分为如下几类:

  • Makefile - 通过 CC 和 CXX 环境变量指定交叉编译器所在路径即可
  • CMake - 通过 CMAKE_TOOLCHAIN_FILE 指定交叉编译器的信息
  • BJam - 主要用于编译 Boost,通过 user-config.jam 来指定编译器信息
  • automake - 主要通过 --host=host 参数指定程序要运行的系统
  • golang - golang 官方提供了非常便利的方法进行交叉编译,只需要指定 GOOS 和 GOARCH 环境变量即可,另外还可以通过 go tool dist list 列出所有合法的 $GOOS/$GOARCH 组合

另外还有像 OpenSSL 这类比较特殊的构建系统,需要查看官方提供的文档和源码来确定交叉编译的方法。