交叉编译工具链实现教程
1. 基础概念
什么是交叉编译工具链?
交叉编译工具链是一套编译器、链接器、库和工具的集合,用于在一个架构(主机)上编译出能在另一个架构(目标)上运行的程序。
关键术语
- Host(主机):运行编译器的系统
- Target(目标):编译出的程序运行的系统
- Build(构建):用于构建工具链的系统
- Triplet(三元组):描述架构的格式,如
arm-linux-gnueabihf
2. 环境准备
2.1 安装依赖工具
# Ubuntu/Debian
sudo apt update
sudo apt install -y build-essential git wget curl \
texinfo bison flex gawk m4 python3 python3-dev \
libncurses5-dev libssl-dev libelf-dev bc
2.2 配置国内镜像源
为了加快下载速度,配置国内镜像源:
# 设置镜像源变量
export MIRROR_TSINGHUA="https://mirrors.tuna.tsinghua.edu.cn"
export MIRROR_USTC="https://mirrors.ustc.edu.cn"
export MIRROR_ALIYUN="https://mirrors.aliyun.com"
export MIRROR_HUAWEI="https://mirrors.huaweicloud.com"
# 创建工作目录,很重要的一步!!!!!!
mkdir -p ~/cross-toolchain
cd ~/cross-toolchain
export WORKSPACE=$(pwd)
export PREFIX=$WORKSPACE/toolchain
export PATH=$PREFIX/bin:$PATH
export TARGET=arm-linux-gnueabihf # 目标架构
2.3 配置包管理器镜像(可选)
# Ubuntu/Debian 配置清华镜像
sudo cp /etc/apt/sources.list /etc/apt/sources.list.backup
sudo sed -i 's|http://archive.ubuntu.com|https://mirrors.tuna.tsinghua.edu.cn|g' /etc/apt/sources.list
sudo sed -i 's|http://security.ubuntu.com|https://mirrors.tuna.tsinghua.edu.cn|g' /etc/apt/sources.list
sudo apt update
3. 下载源码
3.1 下载核心组件
# 创建源码目录
mkdir -p $WORKSPACE/src
cd $WORKSPACE/src
# 使用清华大学镜像源
MIRROR_TSINGHUA="https://mirrors.tuna.tsinghua.edu.cn"
MIRROR_USTC="https://mirrors.ustc.edu.cn"
MIRROR_ALIYUN="https://mirrors.aliyun.com"
# Binutils - 清华镜像
wget ${MIRROR_TSINGHUA}/gnu/binutils/binutils-2.41.tar.xz
tar -xf binutils-2.41.tar.xz
# GCC - 清华镜像
wget ${MIRROR_TSINGHUA}/gnu/gcc/gcc-13.2.0/gcc-13.2.0.tar.xz
tar -xf gcc-13.2.0.tar.xz
# Linux内核头文件 - 清华镜像
wget ${MIRROR_TSINGHUA}/kernel/v5.x/linux-5.4.284.tar.xz
tar -xf linux-5.4.284.tar.xz
# glibc - 清华镜像
wget ${MIRROR_TSINGHUA}/gnu/glibc/glibc-2.42.tar.xz
tar -xf glibc-2.42.tar.xz
3.2 下载GCC依赖(使用国内源)
cd gcc-13.2.0
# 修改下载脚本使用国内镜像
sed -i 's|ftp://gcc.gnu.org/pub/gcc/infrastructure/|https://mirrors.tuna.tsinghua.edu.cn/gnu/|g' contrib/download_prerequisites
sed -i 's|https://gcc.gnu.org/pub/gcc/infrastructure/|https://mirrors.tuna.tsinghua.edu.cn/gnu/|g' contrib/download_prerequisites
./contrib/download_prerequisites
cd ..
3.3 备用下载方案
如果自动下载失败,可手动下载GCC依赖:
cd gcc-13.2.0
# 手动下载依赖包
wget ${MIRROR_TSINGHUA}/gnu/mpfr/mpfr-4.1.0.tar.bz2
wget ${MIRROR_TSINGHUA}/gnu/gmp/gmp-6.2.1.tar.bz2
wget ${MIRROR_TSINGHUA}/gnu/mpc/mpc-1.2.1.tar.gz
wget https://gcc.gnu.org/pub/gcc/infrastructure/isl-0.24.tar.bz2
# 解压并创建符号链接
tar -xf mpfr-4.1.0.tar.bz2 && ln -sf mpfr-4.1.0 mpfr
tar -xf gmp-6.2.1.tar.bz2 && ln -sf gmp-6.2.1 gmp
tar -xf mpc-1.2.1.tar.gz && ln -sf mpc-1.2.1 mpc
tar -xf isl-0.24.tar.bz2 && ln -sf isl-0.24 isl
cd ..
4. 构建步骤
4.1 第一步:构建 Binutils#
mkdir -p $WORKSPACE/build/binutils
cd $WORKSPACE/build/binutils
$WORKSPACE/src/binutils-2.41/configure \
--target=$TARGET \
--prefix=$PREFIX \
--with-sysroot=$PREFIX/$TARGET \
--disable-nls \
--disable-werror
make -j$(nproc)
make install
4.2 第二步:安装Linux内核头文件#
cd $WORKSPACE/src/linux-5.4.284
# 清理之前的安装(如果有)
make ARCH=arm mrproper
# 安装内核头文件到正确位置
make ARCH=arm INSTALL_HDR_PATH=$PREFIX/$TARGET/usr headers_install
# 验证安装是否成功
echo "=== 验证内核头文件安装 ==="
ls -la $PREFIX/$TARGET/usr/include/linux/errno.h
ls -la $PREFIX/$TARGET/usr/include/linux/limits.h
ls -la $PREFIX/$TARGET/usr/include/asm/
# 创建符号链接确保路径一致性
if [ ! -L $PREFIX/$TARGET/include ] && [ -d $PREFIX/$TARGET/usr/include ]; then
ln -sf usr/include $PREFIX/$TARGET/include
fi
4.3 第三步:构建GCC第一阶段(Bootstrap)#
mkdir -p $WORKSPACE/build/gcc-stage1
cd $WORKSPACE/build/gcc-stage1
$WORKSPACE/src/gcc-13.2.0/configure \
--target=$TARGET \
--prefix=$PREFIX \
--with-sysroot=$PREFIX/$TARGET \
--with-newlib \
--without-headers \
--disable-nls \
--disable-shared \
--disable-multilib \
--disable-decimal-float \
--disable-threads \
--disable-libatomic \
--disable-libgomp \
--disable-libquadmath \
--disable-libssp \
--disable-libvtv \
--disable-libstdcxx \
--enable-languages=c
make -j$(nproc) all-gcc all-target-libgcc
make install-gcc install-target-libgcc
4.6 第四步:完整构建glibc#
mkdir -p $WORKSPACE/build/glibc
cd $WORKSPACE/build/glibc
# 如果之前的构建出现链接错误,先清理
make clean 2>/dev/null || rm -rf *
# 设置交叉编译环境变量
export CC="$TARGET-gcc"
export CXX="$TARGET-g++"
export AR="$TARGET-ar"
export RANLIB="$TARGET-ranlib"
# 重新配置(使用更保守的选项)
$WORKSPACE/src/glibc-2.42/configure \
--build=$(uname -m)-pc-linux-gnu \
--host=$TARGET \
--target=$TARGET \
--prefix=$PREFIX/$TARGET \
--with-headers=$PREFIX/$TARGET/usr/include \
--disable-multilib \
--disable-nscd \
--disable-werror \
--enable-kernel=5.4.0 \
--disable-profile \
--without-gd \
libc_cv_forced_unwind=yes \
libc_cv_c_cleanup=yes
# 构建glibc(如果并行编译出错,改用单线程)
make -j$(nproc) || make -j1
# 安装glibc
make install
4.7 第五步:构建完整GCC(GCC第二步)
mkdir -p $WORKSPACE/build/gcc-stage2
cd $WORKSPACE/build/gcc-stage2
# 清理之前可能设置的交叉编译环境变量
unset CC CXX AR RANLIB STRIP
# 重新设置正确的PATH(确保使用刚构建的工具链)
export PATH=$PREFIX/bin:$PATH
# 配置完整的GCC,添加--build参数明确指定构建环境
$WORKSPACE/src/gcc-13.2.0/configure \
--build=$(uname -m)-pc-linux-gnu \
--host=$(uname -m)-pc-linux-gnu \
--target=$TARGET \
--prefix=$PREFIX \
--with-sysroot=$PREFIX/$TARGET \
--disable-nls \
--enable-languages=c,c++ \
--disable-multilib \
--disable-libsanitizer \
--disable-libssp \
--enable-threads=posix \
--enable-shared \
--enable-__cxa_atexit \
--with-arch=armv7-a \
--with-float=hard \
--with-fpu=vfpv3-d16
make -j$(nproc)
make install
4.8 修改库路径为相对路径
错误
~/cross-toolchain2$ $TARGET-gcc -o test test.c
/home/lx/cross-toolchain2/toolchain/lib/gcc/arm-linux-gnueabihf/13.2.0/../../../../arm-linux-gnueabihf/bin/ld: cannot find /home/lx/cross-toolchain2/toolchain/arm-linux-gnueabihf/lib/libc.so.6 inside /home/lx/cross-toolchain2/toolchain/arm-linux-gnueabihf
/home/lx/cross-toolchain2/toolchain/lib/gcc/arm-linux-gnueabihf/13.2.0/../../../../arm-linux-gnueabihf/bin/ld: cannot find /home/lx/cross-toolchain2/toolchain/arm-linux-gnueabihf/lib/libc_nonshared.a inside /home/lx/cross-toolchain2/toolchain/arm-linux-gnueabihf
/home/lx/cross-toolchain2/toolchain/lib/gcc/arm-linux-gnueabihf/13.2.0/../../../../arm-linux-gnueabihf/bin/ld: cannot find /home/lx/cross-toolchain2/toolchain/arm-linux-gnueabihf/lib/ld-linux-armhf.so.3 inside /home/lx/cross-toolchain2/toolchain/arm-linux-gnueabihf
collect2: error: ld returned 1 exit status
~/cross-toolchain2$
cd $PREFIX/..
touch fix.sh && chmod +x fix.sh
#!/bin/bash
# 设置变量
TARGET=$TARGET
PREFIX=$PREFIX
echo "=== 3. 修复 libc.so 链接脚本 ==="
# 备份原文件
cp $PREFIX/$TARGET/arm-linux-gnueabihf/lib/libc.so $PREFIX/$TARGET/arm-linux-gnueabihf/lib/libc.so.backup
######链接脚本改成这样
vim $PREFIX/$TARGET/arm-linux-gnueabihf/lib/libc.so
/* GNU ld script
Use the shared library, but some functions are only in
the static library, so try that secondarily. */
OUTPUT_FORMAT(elf32-littlearm)
GROUP ( libc.so.6 libc_nonshared.a AS_NEEDED ( ld-linux-armhf.so.3 ) )
5. 测试
5.1 简单测试
#!/bin/bash
# 设置环境变量
export PATH="/home/lx/cross-toolchain/toolchain/bin:$PATH"
TARGET=arm-linux-gnueabihf
echo "=== 1. 检查交叉编译器是否可用 ==="
which ${TARGET}-gcc
${TARGET}-gcc --version | head -1
echo ""
echo "=== 2. 检查 sysroot 配置 ==="
${TARGET}-gcc -print-sysroot
echo ""
echo "=== 3. 检查库搜索路径 ==="
${TARGET}-gcc -print-search-dirs | grep libraries
echo ""
echo "=== 4. 简单编译测试 ==="
cd /tmp
echo 'int main(){return 0;}' > test.c
echo "正在编译..."
if ${TARGET}-gcc test.c -o test.arm; then
echo "✓ 编译成功!"
file test.arm
else
echo "✗ 编译失败,详细信息:"
${TARGET}-gcc -v test.c -o test.arm
fi
# 清理
rm -f test.c test.arm
5.2 全面测试
#!/bin/bash
# 设置环境变量
export PATH="/home/lx/cross-toolchain/toolchain/bin:$PATH"
TARGET=arm-linux-gnueabihf
echo "=== 交叉编译工具链全面测试 ==="
echo "时间: $(date)"
echo ""
echo "=== 1. 基本工具测试 ==="
echo "GCC: $(${TARGET}-gcc --version | head -1)"
echo "G++: $(${TARGET}-g++ --version | head -1)"
echo "AS: $(${TARGET}-as --version | head -1)"
echo "LD: $(${TARGET}-ld --version | head -1)"
echo "AR: $(${TARGET}-ar --version | head -1)"
echo "STRIP: $(${TARGET}-strip --version | head -1)"
echo ""
echo "=== 2. C 程序编译测试 ==="
cd /tmp
# C程序测试
cat > test_c.c << 'EOF'
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
printf("Hello from ARM cross-compiled C program!\n");
printf("sizeof(int) = %zu\n", sizeof(int));
printf("sizeof(long) = %zu\n", sizeof(long));
printf("sizeof(void*) = %zu\n", sizeof(void*));
char *str = malloc(100);
strcpy(str, "Dynamic memory allocation works!");
printf("%s\n", str);
free(str);
return 0;
}
EOF
if ${TARGET}-gcc test_c.c -o test_c; then
echo "✓ C程序编译成功"
file test_c
echo "程序大小: $(du -h test_c | cut -f1)"
else
echo "✗ C程序编译失败"
fi
echo ""
echo "=== 3. C++ 程序编译测试 ==="
cat > test_cpp.cpp << 'EOF'
#include <iostream>
#include <vector>
#include <string>
class TestClass {
private:
std::string name;
std::vector<int> numbers;
public:
TestClass(const std::string& n) : name(n) {
for(int i = 0; i < 5; i++) {
numbers.push_back(i * 2);
}
}
void display() {
std::cout << "Hello from ARM cross-compiled C++ program!" << std::endl;
std::cout << "Object name: " << name << std::endl;
std::cout << "Numbers: ";
for(int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;
}
};
int main() {
TestClass obj("ARM-Test");
obj.display();
return 0;
}
EOF
if ${TARGET}-g++ test_cpp.cpp -o test_cpp; then
echo "✓ C++程序编译成功"
file test_cpp
echo "程序大小: $(du -h test_cpp | cut -f1)"
else
echo "✗ C++程序编译失败"
fi
echo ""
echo "=== 4. 静态链接测试 ==="
if ${TARGET}-gcc -static test_c.c -o test_c_static; then
echo "✓ 静态链接编译成功"
file test_c_static
echo "静态程序大小: $(du -h test_c_static | cut -f1)"
else
echo "✗ 静态链接编译失败"
fi
echo ""
echo "=== 5. 优化编译测试 ==="
if ${TARGET}-gcc -O2 -march=armv7-a -mfpu=neon test_c.c -o test_c_optimized; then
echo "✓ 优化编译成功"
file test_c_optimized
echo "优化程序大小: $(du -h test_c_optimized | cut -f1)"
else
echo "✗ 优化编译失败"
fi
echo ""
echo "=== 6. 库依赖检查 ==="
echo "动态链接程序的库依赖:"
${TARGET}-objdump -p test_c | grep NEEDED || echo "无法获取依赖信息"
echo ""
echo "=== 7. 程序头信息 ==="
${TARGET}-readelf -h test_c | head -10
echo ""
echo "=== 8. 清理测试文件 ==="
rm -f test_c test_cpp test_c_static test_c_optimized test_c.c test_cpp.cpp
echo "测试文件已清理"
echo ""
echo "=== 测试总结 ==="
echo "✓ 交叉编译工具链工作正常!"
echo "✓ 可以编译 C 和 C++ 程序"
echo "✓ 支持动态和静态链接"
echo "✓ 支持优化编译"
echo "✓ 生成正确的 ARM 架构程序"
echo ""
echo "工具链位置: /home/lx/cross-toolchain/toolchain/"
echo "使用方法: export PATH=\"/home/lx/cross-toolchain/toolchain/bin:\$PATH\""
echo "然后就可以使用 ${TARGET}-gcc 进行交叉编译了!"
1336

被折叠的 条评论
为什么被折叠?



