wiki.osdev.org 系列之(六)- GCC Cross-Compiler

https://wiki.osdev.org/GCC_Cross-Compiler 难度基本:初级

本教程着重于为您自己的操作系统创建一个GCC交叉编译器。我们在这里构建的这个编译器将有一个通用目标(i686-elf ),它允许您离开当前的操作系统,这意味着不会使用您的主机操作系统的头文件或库。操作系统开发需要交叉编译器,否则会发生很多意想不到的事情,因为编译器假设你的代码运行在你的主机操作系统上。

介绍

一般来说,交叉编译器是在平台A(主机)上运行的编译器,但是为平台B(目标)生成可执行文件。这两个平台在CPU、操作系统和/或可执行格式上可能(但不一定)不同。在我们的例子中,主机平台是您当前的操作系统,目标平台是您将要开发的操作系统。重要的是要认识到这两个平台是不一样的;您正在开发的操作系统总是与您当前使用的操作系统不同。这就是为什么我们需要首先构建一个交叉编译器,否则你肯定会遇到麻烦。

为什么交叉编译器是必要的

主文:我为什么需要交叉编译器?
除非在自己的操作系统上开发,否则需要使用交叉编译器。编译器必须知道正确的目标平台(CPU,操作系统),否则你会遇到麻烦。如果你使用系统自带的编译器,那么编译器完全不会知道它在编译别的东西。一些教程建议使用您的系统编译器,并将许多有问题的选项传递给编译器。这在将来肯定会给你带来很多问题,解决方法是构建一个交叉编译器。如果您已经尝试过在不使用交叉编译器的情况下开发操作系统,请阅读文章《我为什么需要交叉编译器》?。

选择哪个编译器版本

主要文章:构建GCC
推荐使用最新的GCC,因为它是最新最棒的版本。例如,如果您使用GCC 4.6.3构建GCC 4.8.0交叉编译器,您可能会遇到麻烦。如果您的系统编译器没有使用最新的主要GCC版本,我们建议您构建最新的GCC作为您的系统编译器。

您也可以使用旧版本,因为它们通常相当不错。如果您的本地系统编译器不是太老(至少是GCC 4.6.0),您可能希望省去麻烦,只为您的交叉编译器选择最新的次要版本(比如4.6.3,如果您的系统编译器是4.6.1)。

您可以通过调用以下命令来查看当前的编译器版本:

gcc --version

您也许能够使用一个较旧的主要GCC版本来构建一个较新的主要GCC版本的交叉编译器。例如,GCC 4.7.3可能能够构建GCC 4.8.0交叉编译器。但是,如果您想为您的交叉编译器使用最新最好的GCC版本,我们建议您首先引导最新的GCC作为您的系统编译器。使用OS X 10.7或更早版本的个人可能希望投资构建一个系统GCC(输出本机Mach-O),或者升级本地LLVM/Clang安装。0.8 及以上版本的用户应从 Apple 开发者网站安装命令行工具,并使用 Clang 交叉编译 GCC。

选择哪个binutils版本

主要文章:交叉编译器成功构建
我们建议您使用最新最好的Binutils版本。但是,请注意,并不是所有GCC和Binutils的组合都有效。如果遇到麻烦,请使用与您期望的编译器版本大致同时发布的Binutils。您可能至少需要Binutils 2.22,或者最好是最新的2.23.2版本。在当前操作系统上安装了什么版本的Binutils并不重要。例如,您可以通过以下命令找到binutils版本:

ld --version

决定目标平台

主要文章:目标三元组
这个你应该已经知道了。如果您正在学习基本教程,您希望为 i686-elf 构建一个交叉编译器。

关于arm-none-eabi-gcc的说明

apt-get上有针对Debiab/Ubuntu的预构建包gcc-arm-none-eabi,但您不应该使用它,因为它既不包含libgcc.a也不包含像stdint.h这样的独立C头文件。
相反,您应该以 arm-none-eabi 作为 $TARGET 自己构建它。

为构建做准备

难度水平:新手

GNU编译器集合是一个具有依赖性的高级软件。为了构建GCC,您需要以下内容:

  • 类似Unix的环境(Windows用户可以使用Linux子系统或Cygwin)
  • 足够的内存和硬盘空间(看情况,256 MiB 肯定是不够的)。
  • GCC(您希望替换的现有版本)或其他系统 C 编译器。
  • G++(如果构建GCC >= 4.8.0的版本),或者其它系统C++编译器
  • Make
  • Bison
  • Flex
  • GMP
  • MPFR
  • MPC
  • Texinfo
  • ISL (optional)
  • CLooG (optional)

Installing Dependencies

↓ Dependency / OS → Source Code Debian (Ubuntu, Mint, WSL, ...) Gentoo Fedora Cygwin OpenBSD Arch
How to install Normally sudo apt install foo sudo emerge --ask foo sudo dnf install foo Cygwin GUI setup doas pkg_add foo pacman -Syu foo
Compiler N/A build-essential sys-devel/gcc gcc gcc-c++ mingw64-x86_64-gcc-g++ / mingw64-i686-gcc-g++ Preinstalled base-devel
Make N/A build-essential sys-devel/make make make Preinstalled base-devel
Bison [1] bison sys-devel/bison bison bison ? base-devel
Flex [2] flex sys-devel/flex flex flex ? base-devel
GMP [3] libgmp3-dev dev-libs/gmp gmp-devel libgmp-devel gmp gmp
MPC [4] libmpc-dev dev-libs/mpc libmpc-devel libmpc-devel libmpc libmpc
MPFR [5] libmpfr-dev dev-libs/mpfr mpfr-devel libmpfr-devel mpfr mpfr
Texinfo [6] texinfo sys-apps/texinfo texinfo texinfo texinfo base-devel
CLooG (Optional) CLooG libcloog-isl-dev dev-libs/cloog cloog-devel libcloog-isl-devel N/A N/A
ISL (Optional) [7] libisl-dev dev-libs/isl isl-devel libisl-devel N/A N/A

你需要安装 Texinfo 来构建 Binutils。您需要安装 GMP、MPC 和 MPFR 来构建 GCC。 GCC 可以选择使用 ClooG 和 ISL 库。

例如,您可以通过运行 shell 命令在 Debian 上安装 libgmp3-dev:sudo apt install libgmp3-dev

注意:已知 Texinfo 的 5.x(或更高版本)版本与当前的 Binutils 2.23.2 版本(和更早版本)不兼容。您可以使用 makeinfo --version 检查当前版本。如果您的版本太新并且在构建过程中遇到问题,您将需要使用 Binutils 2.24 版本(或更高版本)或安装旧版本的 Texinfo(可能通过从源代码构建)并在Binutils构建之前和构建过程中将其添加到您的路径中。

注意:ISL 0.13 版(或更高版本)与当前的 ClooG 0.18.1 版本(和更早版本)不兼容。使用 ISL 0.12.2 版,否则构建将失败。

下载源代码

将所需的源代码下载到合适的目录中,比如 $HOME/src:

您可以通过访问 Binutils 网站或直接访问 GNU 主镜像来下载所需的 Binutils 版本。
您可以通过访问 GCC 网站或直接访问 GNU 主镜像来下载所需的 GCC 版本。

注意:使用的版本控制方案是每个句号分隔一个完整的数字,即 Binutils 2.20.0 比 2.9.0 更新。如果您还没有遇到这种(非常常见的)版本控制方案,这可能会令人困惑,在查看按字母数字排序的 tarball 列表时: 列表底部的文件不是最新版本!获取最新版本的一种简单方法是按最后修改日期排序并滚动到底部。

Linux用户构建系统编译器

您的发行版可能会提供它自己的打了补丁的GCC和Binutils,它们是为在您的特定Linux发行版上工作而定制的。您应该能够使用上面的源代码构建一个有效的交叉编译器,但是您可能无法为您当前的Linux发行版构建一个新的系统编译器。在这种情况下,请尝试更新的GCC版本,或者获取打了补丁的源代码。

Gentoo 用户

Gentoo 提供 crossdev 来建立一个交叉开发工具链:

emerge -av crossdev
   crossdev --help
   PORTDIR_OVERLAY="/usr/local/crossdev" crossdev --stage1 --binutils <binutils-version> --gcc <gcc-version> --target <target>

这将把GCC交叉编译器安装到一个“插槽”中,即与已经存在的编译器版本放在一起。你可以用这种方式安装多个交叉编译器,只需改变目标名称。一个不幸的缺点是,它还会引入gentoo补丁并传递与官方GCC交叉编译器设置不同的附加配置选项,并且它们的行为可能不同。

编译成功完成后,可以通过 -gcc 调用你的交叉编译器。如果需要,还可以使用gcc-config在编译器版本之间切换。不要用交叉编译器替换你的系统编译器。包管理器还将在更新可用时立即提出建议。

你可以通过调用 crossdev --clean 来卸载交叉编译器。阅读交叉开发文档以获取更多信息。

请注意,binutils 和 gcc 的版本号是 Gentoo 软件包版本,即“官方”(GNU)版本可能有一个后缀,用于解决由 Gentoo 维护者提供的附加补丁集。 (例如,--binutils 2.24-r3 --gcc 4.8.3 是撰写本文时最新的稳定软件包对。)您可以省略版本号以使用最新的可用软件包。

Portage使用覆盖来存储不属于原始包管理的包。Crossdev需要一个覆盖,在构建binutils和gcc包之前,可以在这里存储它们。您可以适当地配置一个,或者您可以使用PORTDIR_OVERLAY来指出它应该在哪里保存它的包管理器文件。使用PORTDIR_OVERLAY对于现有的覆盖图来说不是一个好主意,但是到那时你应该已经知道你是如何设置它们的,以及如何正确地设置它们。参见[8]

macOS用户

macOS 用户需要更换 libiconv,因为系统 libiconv 严重过时。macOS 用户可以通过访问 libiconv 网站或直接访问 GNU 主 FTP 镜像来下载最新的 libiconv 版本。否则,在 OS X 10.4 和 10.5 上编译 GCC 4.3 或更高版本时,您可能会遇到与 libiconv 相关的未解决的符号错误。

安装一个新版本(自己编译或使用 MacPorts)并将 --with-libiconv-prefix=/opt/local(或 /usr/local,如果你自己编译)添加到 GCC 的 ./configure 行。或者,您可以将 libiconv 源代码放置为 gcc-x.y.z/libiconv,它将作为 GCC 编译过程的一部分进行编译。 (这个技巧也适用于 MPFR、GMP 和 MPC)。

Binutils 和 GCC 的 makefile 使用 $(CC) 变量来调用编译器。在 OS X 上,这默认解析为 gcc,但实际上是 clang。在OS X 10.8之前,Xcode的命令行工具包不能构建一个工作的GCC。在 OS X 10.8 之前,Xcode 的命令行工具包附带的 Clang 无法构建工作的 GCC。运行 OS X 10.7 或更低版本的用户可能需要从 Homebrew 或 Apple 网站上的某个位置查找并安装 GCC。您可以尝试使用某些 macOS 版本上预装的旧 GCC。

# This is only necessary for OS X users running 10.7 or below.
export CC=/usr/bin/gcc-4.2
export CXX=/usr/bin/g++-4.2
export CPP=/usr/bin/cpp-4.2
export LD=/usr/bin/gcc-4.2

编译并安装交叉编译器后,您将需要取消设置这些环境变量。

Lion 用户注意事项:如果您使用 Lion(或更高版本),您可能没有“真正的”GCC,因为 Apple 从 Xcode 包中删除了它,但您仍然可以安装它。您可以通过 Homebrew 或从源代码编译来完成,两者都在 StackExchange 答案中得到了完美的描述。

Maverick 用户注意事项:您可以使用 Xcode 5.1.1 构建 binutils-2.24 和 gcc-4.8.3(可能的其他版本)。请注意,官方不支持使用 LLVM 构建 GCC,并且可能会导致有趣的错误,如果您愿意冒这个风险并节省构建 host-gcc 的时间来编译跨 gcc,请遵循下面的方法。使用 MacPorts 安装 GMP、MPFR、Mpc。

sudo port install gmp mpfr libmpc
../binutils-2.24/configure --prefix=$PREFIX \
--target=$TARGET \
--enable-interwork --enable-multilib \
--disable-nls --disable-werror
../gcc-4.8.3/configure --prefix=$PREFIX \
--target=$TARGET \
--disable-nls \
--enable-languages=c,c++ --without-headers \
--enable-interwork --enable-multilib \
--with-gmp=/usr --with-mpc=/opt/local --with-mpfr=/opt/local

注意:port 的 GMP 存在问题,因此使用来自 /usr 的 OS X 版本。

Windows用户

Windows用户需要建立一个类似Unix的环境,如MinGW或Cygwin。很有必要研究一下像Linux这样的系统,看看它们是否符合您的需求,因为您通常会在操作系统开发中使用许多类似Unix的工具,这对于类似Unix的操作系统来说要容易得多。如果您刚刚安装了基本的Cygwin包,您必须再次运行setup.exe并安装以下包:GCC、G++、Make、Flex、Bison、Diffutils、libintl-devel、libgmp-devel、libmpfr-devel、libmpc-devel、Texinfo

MinGW + MSYS是一个选项,因为它针对的是原生Windows API而不是POSIX仿真层,所以工具链会稍微快一点。有些软件包不能在MSYS下正常运行,因为它们不是为Windows设计的。就本教程而言,适用于Cygwin的一切也适用于MSYS,除非另有说明。确保安装了C和C++编译器,以及MSYS基本系统。

与Windows 10周年更新一起发布的“Windows Subsystem for Linux (Beta)”也是使用交叉编译器的一个选项。(2016年8月8日用GCC 6.1.0和Binutils 2.27测试)这个交叉编译器运行速度相当快,虽然处于测试阶段,但它可能不是理想的永久开发平台。

Cygwin注意:Cygwin在其 bash $PATH 中包含了您的 Windows %PATH% 。如果您之前使用过 DJGPP,这可能会导致混淆,例如在 Cygwin bash 命令行上调用 GCC 仍会调用 DJGPP 编译器。卸载 DJGPP 后,您应该删除 DJGPP 环境变量并从 %PATH% 中清除 C:\djgpp 条目(或安装它的位置)。同样,在系统路径变量中混合构建环境可能不是一个好主意。

MinGW 注意:有关构建交叉工具链的一些 MinGW 特定信息可以在 MinGW 主页上托管的交叉编译器操作指南页面上找到。

Windows Subsystem for Linux (Beta)注意:您不能在 /mnt/c/(或 /mnt/“x”)区域中使用交叉编译器,因为尝试在那里编译交叉编译器会产生错误,而构建到 $HOME/opt/cross 完美运行。这已通过 Windows 更新 KB3176929 修复

OpenBSD用户

OpenBSD用户可能需要从 ports 安装“gcc”包,因为基本系统的GCC非常过时。如果您想要构建GCC,请尝试使用 ports 的版本,而不是可用的最新版本,并将 ports 中的所有补丁应用到您的构建中。另外,如果在编译 lto-plugin 期间构建失败,一个临时的解决方案是在构建 GCC 的配置阶段通过添加 --disable-lto 完全禁用 LTO

构建

我们构建了一个在您的主机上运行的工具集,它可以将源代码转换成目标系统的目标文件。

你需要决定在哪里安装你的新编译器。将它安装到系统目录中是危险的,也是非常糟糕的想法。您还需要决定新的编译器是应该全局安装还是只为您安装。如果您想只为你自己安装它(推荐),安装到 $HOME/opt/cross 通常是个好主意。如果您想全局安装它,将它安装到 /usr/local/cross 通常是个好主意。

请注意,我们在源目录树之外构建所有内容,这被认为是一种良好的做法。有些包只支持外部构建,有些只支持内部构建,有些两者都支持(但可能不提供广泛的make检查)。在源目录树中构建GCC失败得很惨,至少对于旧版本是这样。

准备

export PREFIX="$HOME/opt/cross"
export TARGET=i686-elf
export PATH="$PREFIX/bin:$PATH"

我们将安装前缀添加到当前shell会话的 PATH 中。这确保了一旦我们构建了新的binutils,编译器就能够检测到它们。

该前缀将配置构建过程,以便您的交叉编译器环境的所有文件都以 $HOME/opt/cross 结尾。您可以将该前缀更改为您喜欢的任何内容(例如,/opt/cross 或 $HOME/cross 等)。如果您拥有管理员访问权限,并且希望所有用户都可以使用交叉编译器工具链,那么您可以将它安装到 /usr/local 前缀中——如果您愿意更改系统配置,也可以安装在 /usr/local/cross 前缀中这样这个目录就在所有用户的搜索路径中。从技术上讲,您甚至可以直接安装到 /usr,这样您的交叉编译器将与您的系统编译器放在一起,但出于多种原因不建议这样做(例如,如果您弄错了目标,可能会覆盖您的系统编译器,或者与您的系统的包管理发生冲突)。

Binutils

cd $HOME/src
 
mkdir build-binutils
cd build-binutils
../binutils-x.y.z/configure --target=$TARGET --prefix="$PREFIX" --with-sysroot --disable-nls --disable-werror
make
make install

这会编译 binutils(汇编器、反汇编器和各种其他有用的东西),可在您的系统上运行,但以 $TARGET 指定的格式处理代码

--disable-nls 告诉 binutils 不包括本地语言支持。这基本上是可选的,但减少了依赖性和编译时间。它还将导致英语诊断,当您提出问题时,论坛上的人会理解。 ;-)

--with-sysroot 告诉 binutils 通过将交叉编译器指向默认的空目录来启用 sysroot 支持。默认情况下,如果没有好的技术原因,链接器拒绝使用sysroots,而 gcc 能够在运行时处理这两种情况。这将在以后有用。

GCC

See also the offical instructions for configuring gcc. Now, you can build GCC.

cd $HOME/src
 
# The $PREFIX/bin dir _must_ be in the PATH. We did that above.
which -- $TARGET-as || echo $TARGET-as is not in the PATH
 
mkdir build-gcc
cd build-gcc
../gcc-x.y.z/configure --target=$TARGET --prefix="$PREFIX" --disable-nls --enable-languages=c,c++ --without-headers
make all-gcc
make all-target-libgcc
make install-gcc
make install-target-libgcc

我们构建了 libgcc,这是一个编译器期望在编译时可用的低级支持库。与 libgcc 的链接提供整数、浮点、小数、堆栈展开(用于异常处理)和其他支持功能。请注意,我们并没有简单地运行 make && make install,因为那样会构建得太多,并不是 gcc 的所有组件都可以针对您尚未完成的操作系统。

--disable-nls 与上面的 binutils 相同。

--without-headers 告诉 GCC 不要依赖目标中存在的任何 C 库(标准或运行时)。

--enable-languages 告诉 GCC 不要编译它支持的所有其他语言前端,而只编译 C(和可选的 C++)。

构建交叉编译器需要一段时间。

如果您正在为 x86-64 构建交叉编译器,您可能需要考虑构建没有“红色区域”的 Libgcc:Libgcc_without_red_zone

使用新的编译器

现在你有了一个“裸”交叉编译器。它还不能访问C库或C运行时,所以您不能使用任何标准的includes或创建可运行的二进制文件。但是对于编译您即将制作的内核来说,这已经足够了。您的工具集位于 $HOME/opt/cross(或您将 $PREFIX 设置为的位置)中。例如,您有一个安装为$HOME/opt/cross/bin/$TARGET-GCC的GCC可执行文件,它会为您的 TARGET 创建程序

现在,您可以通过调用如下代码来运行新的编译器:

$HOME/opt/cross/bin/$TARGET-gcc --version

注意这个编译器为什么不能编译普通的C程序。每当您想要 #include 任何标准头文件时,交叉编译器都会抛出错误(除了少数实际上与平台无关的、由编译器自己生成的头文件)。这是完全正确的——你还没有目标系统的标准库!

C标准定义了两种不同的执行环境——“独立”和“托管”。虽然对于一般的应用程序程序员来说,这个定义可能相当模糊,但是当你进行操作系统开发时,这个定义是非常清晰的:内核是“独立的”,你在用户空间中做的一切都是“托管的”。“独立”环境只需要提供C库的一个子集: float.h、iso646.h、limits.h、stdalign.h、stdarg.h、stdbool.h、stddef.h、stdint.h和stdnoreturn.h(从C11开始)。所有这些都“仅”由 typedef#define组成,因此您可以在没有单个.c 文件的情况下实现它们。

要使用新的编译器,只需调用 $TARGET-gcc,通过键入以下命令将$HOME/opt/cross/bin 添加到您的 $PATH 中:

export PATH="$HOME/opt/cross/bin:$PATH"

此命令会将您的新编译器添加到此 shell 会话的 PATH 中。如果您希望永久使用它,请将 PATH 命令添加到您的 ~/.profile 配置 shell 脚本或类似脚本中。有关更多信息,请参阅您的 shell 文档。

您现在可以继续完成引导您到此处的 Bare Bones 教程变体,并使用新的交叉编译器完成它。如果您构建了一个新的 GCC 版本作为您的系统编译器并使用它来构建交叉编译器,您现在可以安全地卸载它,除非您希望继续使用它。

故障排除

一般来说,请验证您是否仔细阅读了说明并准确键入了命令。不要跳过说明。如果您使用新的 shell 实例,如果您没有通过将其添加到您的 shell 配置文件使其永久化,您将不得不再次设置您的 PATH 变量。如果编译似乎真的搞砸了,请键入 make distclean,然后重新启动 make 过程。确保您的 un-archiver 不会更改换行符。

ld: cannot find -lgcc

您指定要通过 -lgcc' 开关将 GCC 低级运行时库链接到可执行文件,但忘记构建和正确安装库。
如果您在安装 libgcc 时没有收到警告或错误,但仍然有问题,您可以将库复制到项目中并将用 -L 链接它。 -lgcc
libgcc 位于 $PREFIX/lib/gcc/$TARGET/<gcc-version>/libgcc.a

Binutils 2.9

按字母顺序排列在顶部或底部的不一定是最新版本。在 2.9 之后是 2.10、2.11、2.12,然后还有更多更新的版本,并且越来越有可能构建或支持您选择的 GCC 版本。

Building GCC: the directory that should contain system headers does not exist

在构建 mingw32 目标时,您可能会遇到此错误,例如 x86_64-w64-mingw32。找不到的有问题的目录是 $SYSROOT/mingw/include。如果您查看您的 sysroot,您当然会意识到不存在这样的文件夹。

解决方案是简单地创建空文件夹:

mkdir -p $SYSROOT/mingw/include
mkdir -p $SYSROOT/mingw/lib

这将允许构建继续进行。发生这种情况的原因是 mingw32(和 mingw 本身)将 INCLUDE_PATHLIBRARY_PATH 配置为,可以猜到,/mingw/include/mingw/lib,而不是默认的 /usr/include/usr/lib。为什么即使这些文件夹中不需要任何东西,构建也会失败,以及为什么它不创建它们,这超出了我的理解。

GCC libsanitizer failing to build

有时 GCC 无法构建 libsanitizer,如果发生这种情况,请将 --disable-libsanitizer 附加到 configure 命令。
这仅适用于构建托管编译器。

更先进

使用这个简单的交叉编译器在相当长的一段时间内就足够了,但在某些时候你会希望编译器自动包含你自己的系统头文件和库。从这里开始,为你自己的操作系统构建一个特定于操作系统的工具链

See Also

Articles

External Links

Prebuilt Toolchains

These were built by people in the OSdev community for their own building needs and shared at will, without guaranteeing any support or that it will even work on your setup. YMMV.

Latests versions for Linux (many arch)

For Linux i686 host

For Linux x86_64 host

The packages from phillid.tk below have been shrunk to about 10 MiB for each pair of packages (GCC & Binutils). Please note that this has been achieved by enabling only the C front-end for GCC. If you're going to write your OS in any language but C or Assembly, these packages aren't for you. These are actually Pacman packages, but untarring them to / and rm-ing /.MTREE and other clutter dotfiles contained in the package will work the same.

For Windows host

For Windows Subsystem for Linux (Beta) host

  • i686-elf 6.1.0 target (extracts to a directory called "cross", don't forget to install 'make' - I would recommend "apt-get install build-essential" to also add additional useful tools)

For macOS host

x86_64-elf binutils and gcc (canonical target name x86_64-pc-elf) can be installed from homebrew:

$ brew install x86_64-elf-gcc

i686-elf toolchain is also available in homebrew.

ARM prebuilt toolchains for multiple host platforms

ARM provides it's own prebuilt toolchain based upon GNU utilities for development targeting ARM systems.

Docker image


标题:wiki.osdev.org 系列之(六)- GCC Cross-Compiler
作者:糖醋鱼
地址:https://expoli.tech/articles/2022/06/10/1654861381912.html

    评论
    0 评论
avatar

取消