Linux 交叉编译实践笔记

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】为准
###############################################################################
相关推荐
H Journey5 分钟前
Linux su 命令核心用法总结
java·linux·服务器·su
CTO Plus技术服务中18 分钟前
Flink运维与开发教程
大数据·运维·flink
啦啦啦小石头27 分钟前
docker添加用户权限不使用sudo
运维·docker·容器
PPPPPaPeR.30 分钟前
环 境 变 量
linux·运维·windows
HalvmånEver31 分钟前
Linux:线程创建与终止上(线程五)
java·linux·jvm
嵌入小生00732 分钟前
双向链表、双向循环链表之间的异同---嵌入式入门---Linux
linux·c语言·数据结构·链表·嵌入式·小白
H Journey35 分钟前
Linux sudo 命令完全指南
linux·运维·服务器·sudo
CTO Plus技术服务中36 分钟前
分布式存储HBase开发与运维教程
运维·分布式·hbase
开开心心_Every40 分钟前
家常菜谱软件推荐:分类齐全无广告步骤详细
linux·运维·服务器·华为od·edge·pdf·华为云
i建模40 分钟前
在 Arch Linux 中安装 **Xorg 服务器**
linux·运维·服务器