x86_64 → ARM64(aarch64)
1. 什么是交叉编译
在一种架构的机器上,生成另一种架构可执行程序
| 项目 | 示例 |
|---|---|
| 构建机(build) | x86_64 Linux |
| 目标机(host / target) | ARM64 Linux |
| 编译器 | aarch64-linux-gnu-gcc |
| 产物 | ARM64 ELF |
2. 交叉编译三要素(核心)
| 要素 | 作用 |
|---|---|
| Toolchain | 决定生成哪种架构的代码 |
| Sysroot | 决定"用哪套头文件 / 库" |
| Build System | 决定如何查找工具链与库 |
一句话总结:
Toolchain 决定"能不能编",Sysroot 决定"能不能跑"
3. 基础示例(无第三方依赖)
3.1 安装 ARM64 工具链(Ubuntu)
sudo apt install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
生成的关键工具:
aarch64-linux-gnu-gcc aarch64-linux-gnu-g++ aarch64-linux-gnu-ld
3.2 示例代码
#include <stdio.h>
int main()
{
printf("Hello ARM64\n");
return 0;
}
3.3 编译与验证
aarch64-linux-gnu-gcc hello.c -o hello_arm64 file hello_arm64
输出:
ELF 64-bit LSB executable, ARM aarch64
4. Sysroot(工程级必备)
4.1 什么是 sysroot
目标系统"根目录"的最小拷贝
包含:
-
/lib -
/usr/include -
/usr/lib
4.2 获取 sysroot(推荐方式)
rsync -avz root@arm-board:/lib /opt/arm-sysroot/
rsync -avz root@arm-board:/usr/include /opt/arm-sysroot/usr/
rsync -avz root@arm-board:/usr/lib /opt/arm-sysroot/usr/
目录结构:
/opt/arm-sysroot
├── lib
└── usr
├── include
└── lib
4.3 使用 sysroot 编译
bash
aarch64-linux-gnu-gcc --sysroot=/opt/arm-sysroot hello.c -o hello_arm64
5. CMake 交叉编译(常用)
5.1 Toolchain 文件(关键)
aarch64-toolchain.cmake
bash
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)
set(CMAKE_SYSROOT /opt/arm-sysroot)
set(CMAKE_FIND_ROOT_PATH /opt/arm-sysroot)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
5.2 构建流程
bash
mkdir build-arm64 && cd build-arm64
cmake .. -DCMAKE_TOOLCHAIN_FILE=../aarch64-toolchain.cmake
make -j
6. Autotools 项目(configure)
bash
./configure \
--host=aarch64-linux-gnu \
--build=x86_64-linux-gnu \
CC=aarch64-linux-gnu-gcc
7. 运行与验证
7.1 拷贝到 ARM 设备
scp hello_arm64 root@arm:/tmp
7.2 使用 qemu 在 x86 上运行(调试神器)
sudo apt install qemu-user qemu-aarch64 ./hello_arm64
带动态库:
qemu-aarch64 -L /opt/arm-sysroot ./hello_arm64
8. 常见问题与踩坑
8.1 glibc 版本不匹配
表现:
version `GLIBC_2.xx' not found
原因:
- 工具链 glibc ≠ 目标系统 glibc
结论:
Toolchain 和 sysroot 必须同源
8.2 CMake 找到宿主机库
解决:
bash
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
8.3 pkg-config 找错架构
bash
export PKG_CONFIG_SYSROOT_DIR=/opt/arm-sysroot
export PKG_CONFIG_PATH=/opt/arm-sysroot/usr/lib/pkgconfig
9. 调试工具
| 工具 | 用途 |
|---|---|
| file | 查看二进制架构 |
| readelf | 查看依赖库 |
| qemu-aarch64 | x86 运行 ARM 程序 |
| aarch64-linux-gnu-gdb | 交叉调试 |
10. 一句话总结
交叉编译不是"换个 gcc",而是:
用对工具链 + 用对 sysroot + 禁止混入宿主库
交叉编译(ARM / x86)下 ldd 为什么不准 + 正确排错方式
bash
###############################################################################
# 交叉编译场景下的动态库排错 Demo(ARM / x86)
# 重点:为什么 ldd 不准?正确怎么查?
###############################################################################
###############################################################################
# 一、问题背景
###############################################################################
# 你在 x86_64 Linux 上交叉编译了一个 ARM 程序:
# - 构建机:x86_64
# - 目标机:ARMv8 (aarch64)
#
# 常见错误认知:
# "我在构建机上 ldd 一下 ARM 程序看看依赖"
#
# 结论:
# ❌ 这是错的,ldd 在交叉场景下【不可靠 / 直接不可用】
###############################################################################
###############################################################################
# 二、为什么 ldd 在交叉编译下不准?
###############################################################################
# ldd 的本质:
# - 使用【当前系统的动态链接器】模拟加载
#
# 问题点:
# - x86 系统的动态链接器:/lib64/ld-linux-x86-64.so.2
# - ARM 程序需要的动态链接器:ld-linux-aarch64.so.1
#
# 结果:
# 1) ldd 可能直接报错
# 2) ldd 显示 not found(但目标机其实能跑)
# 3) ldd 输出完全不可信
###############################################################################
###############################################################################
# 三、错误示例(不要这样做)
###############################################################################
# 在 x86 构建机上对 ARM 程序使用 ldd
ldd my_arm_binary
# 可能输出:
# not a dynamic executable
# 或
# libgrpc++.so.1 => not found
# 说明:
# - 不是 ARM 程序真有问题
# - 而是 x86 的 ldd 根本不会用 ARM 的链接器
###############################################################################
###############################################################################
# 四、正确姿势 1:readelf(最重要,首选)
###############################################################################
# readelf 不会尝试运行程序
# 它只是"读 ELF 文件结构",完全架构无关
###############################################################################
# 1️⃣ 查看 ELF 目标架构
readelf -h my_arm_binary
# 关键输出示例:
# Machine: AArch64
# 说明:
# - 确认这是 ARM 程序,而不是编错架构
# 2️⃣ 查看程序依赖哪些共享库(NEEDED)
readelf -d my_arm_binary | grep NEEDED
# 示例输出:
# 0x0000000000000001 (NEEDED) Shared library: [libgrpc++.so.1]
# 0x0000000000000001 (NEEDED) Shared library: [libprotobuf.so.32]
# 0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
# 说明:
# - 这才是【真实的运行期依赖】
# - 不依赖当前系统架构
###############################################################################
###############################################################################
# 五、正确姿势 2:确认目标机动态链接器
###############################################################################
# 查看程序需要哪个动态链接器
readelf -l my_arm_binary | grep interpreter
# 示例输出:
# [Requesting program interpreter: /lib/ld-linux-aarch64.so.1]
# 说明:
# - 目标机必须存在这个文件
# - 否则程序直接起不来
###############################################################################
###############################################################################
# 六、正确姿势 3:目标机上用 ldd(最权威)
###############################################################################
# 把程序和依赖拷到目标 ARM 设备上
###############################################################################
# 在 ARM 设备上执行
ldd my_arm_binary
# 示例输出:
# libgrpc++.so.1 => /usr/lib/libgrpc++.so.1
# libprotobuf.so.32 => /usr/lib/libprotobuf.so.32
# libc.so.6 => /lib/libc.so.6
# 说明:
# - 这是唯一"100%可信"的 ldd 结果
###############################################################################
###############################################################################
# 七、目标机缺库时的修复方式
###############################################################################
# 1️⃣ 确认库文件是否存在
ls /usr/lib/libgrpc++.so.1
# 2️⃣ 系统是否认识该库
ldconfig -p | grep grpc
# 3️⃣ 若库在自定义目录(例如 /opt/lib)
sudo vi /etc/ld.so.conf.d/custom.conf
# 写入:
# /opt/lib
# 更新缓存
sudo ldconfig
# 再次检查
ldconfig -p | grep grpc
###############################################################################
###############################################################################
# 八、交叉编译工程级"正确排错顺序"
###############################################################################
# 在构建机(x86):
# 1. readelf -h → 看架构
# 2. readelf -d | NEEDED → 看依赖库名
# 3. readelf -l | interpreter → 看动态链接器
#
# 在目标机(ARM):
# 4. ldd → 看实际加载路径
# 5. ldconfig -p → 看系统是否认识库
# 6. sudo ldconfig → 修复 not found
###############################################################################
###############################################################################
# 九、记住这 4 句"工程级结论"
###############################################################################
# 1️⃣ ldd ≠ ELF 分析工具,它是"运行期模拟器"
# 2️⃣ 交叉编译场景下,构建机 ldd 基本不可信
# 3️⃣ readelf 是交叉编译下的第一选择
# 4️⃣ 最终结果一定以【目标机 ldd】为准
###############################################################################