前置知识
树莓派已经自带了GCC编译器,为什么还要多此一举搭建交叉编译环境?
Linux raspberrypi 5.15.76-v71l+
, Raspbian自带的GCC版本是
10.2.1
,而最新的GCC release版本已经到
12.2.0
了。)
各大Linux发行版的软件源里面有现成的gcc-arm-xxx-guneabi交叉编译工具,arm/linaro等公司也发布了免费的GCC二进制编译套件,直接安装不就行了吗,为什么还要从源码编译Binutils,Glibc和GCC?
主要原因是各大Linux发行版的软件源里面的gcc-arm-xxx-guneabi编译套件中的glibc版本和树莓派系统自带的glibc的版本不一致,导致在PC上交叉编译出来的可执行文件拷贝到树莓派上后运行报错GLIBC的版本不匹配。
root@raspberrypi:/home/jtang# ./helloworld
./helloworld: /lib/arm-linux-gnueabihf/libc.so.6: version `GLIBC_2.34' not found (required by ./helloworld)
笔者写这边博文的时候,Raspbian自带的GLIBC版本是2.31,而目前GLIBC的最新版本已经是2.36。
root@raspberrypi:~# ldd --version
ldd (Debian GLIBC 2.31-13+rpt2+rpi1+deb11u5) 2.31
由于GLIBC直接和Linux Kernal打交道,安全性非常重要,所以像arm/linaro等公司发布的二进制交叉编译套件中的GLIBC版本都是最新的,跟树莓派有严重的配套问题。
我们要自己构建一套树莓派的交叉编译套件,宿主机是发行版Linux(笔者选用Ubuntu20.04.5 LTS),目标机是树莓派。
任务细分如下:
交叉编译套件的Binutils版本和树莓派系统自带的一致,为2.35.2
root@raspberrypi:~# ld -v
GNU ld (GNU Binutils for Raspbian) 2.35.2
交叉编译套件的GLIBC版本和树莓派系统自带的一致,为2.31
root@raspberrypi:~# ldd --version
ldd (Debian GLIBC 2.31-13+rpt2+rpi1+deb11u5) 2.31
这是一个自由软件;请见源代码的授权条款。本软件不含任何没有担保;甚至不保证适销性
或者适合某些特殊目的。
由 Roland McGrath 和 Ulrich Drepper 编写。
交叉编译套件的GCC版本为当前最新的12.2.0
PC:Windows Linux 子系统 Ubuntu20.04.5 LTS
只要是Linux发行版都可以
root@T-T-Desktop:~# uname -a
Linux T-T-Desktop 5.10.102.1-microsoft-standard-WSL2 #1 SMP Wed Mar 2 00:30:59 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
硬件:Raspberry Pi 400
OS: Raspberry Pi OS(32bit)
后续步骤可以参考傻瓜教程Building GCC as a cross compiler for Raspberry Pi | Solarian Programmer,笔者补充一下几点可供参考:
原文中安装的是GCC8.3和GCC-10, 我们需要安装的是GCC10.3和GCC12.2,之所以要装2个GCC,原文中的解释是GLIBC和GCC的构建相互依赖,编译GLIBC最好要用同时期发布的GCC,否则会有莫名其妙的编译问题,反之亦然。所以我们先安装GCC10.3和GLIBC2.31,然后再编译出GCC12.2,这样编译出来的GCC12.2就可以依赖GLIBC2.31了。
国内下载Binutils, Glibc, Gcc源码可以从清华大学开源网站下载,没必要从gnu下载。
wget https://mirrors.tuna.tsinghua.edu.cn/gnu/binutils/binutils-2.35.2.tar.gz
wget https://mirrors.tuna.tsinghua.edu.cn/gnu/glibc/glibc-2.31.tar.gz
wget https://mirrors.tuna.tsinghua.edu.cn/gnu/gcc/gcc-10.3.0/gcc-10.3.0.tar.gz
wget https://mirrors.tuna.tsinghua.edu.cn/gnu/gcc/gcc-12.2.0/gcc-12.2.0.tar.gz
下载树莓派的Linux内核代码,没必要用git clone,速度慢,而且github设置代理后在WSL上使用git clone有各种各样奇怪的问题,建议直接在github网站上下载zip包,然后在WSL上解压即可。
【非常重要】
如果使用了Windows的Linux子系统作为宿主机环境,那么在开始Binutils, GCC和GLIBC构建之前,一定要设置文件名大小写敏感属性。因为Windows对文件名大小写不敏感,stamp.osT和stamp.oST被Windows识别为同一个文件,无法在同一个文件下共存;而Linux是大小写敏感的,所以GLIBC在编译过程中会生成很多仅仅是大小写不一致的多个文件,eg: stamp.osT和stamp.oST;如果不设置文件名大小写敏感属性,那么WSL上构建会报如下错误:
mv: cannot stat '/mnt/d/JTang_Documents/01_c++/raspberry/gcc_all/build-glibc/dlfcn/stamp.oST': No such file or directory
undefined reference to rtld_errno
设置WSL对大小写敏感的方法如下:
a. 首先在WSL下编辑/etc/wsl.conf文件,添加automount相关选项
先执行vi /etc/wsl.conf
然后添加如下内容
[automount]
enabled = true
options = "metadata, mask=22,fmask=11,case=force"
mountFsTab = false
b. 回到Windows下打开命令行窗口,执行如下命令
reg.exe add HKLM\SYSTEM\CurrentControlSet\Services\lxss /v DrvFsAllowForceCaseSensitivity /t REG_DWORD /d 1
附:WSL下设置文件名大小写敏感属性,详情可以参考微软帮助Case Sensitivity | Microsoft Learn
构建GCC之前,修改源码,添加PATH_MAX
定义,否则可能会报错PATH_MAX未定义。(或者也可以先修改,等报错后再改)
笔者这边构建GCC-10.3.0的sanitizer模块时报错了,于是添加PATH_MAX定义
vi /mnt/d/JTang_Documents/01_c++/raspberry/gcc_all/gcc-10.3.0/libsanitizer/asan/asan_linux.cpp
找到使用PATH_MAX的地方,在其上方添加
#ifndef PATH_MAX
#define PATH_MAX 4096
#endif