GCC Cross-Compiler

来自osdev
Zhang3讨论 | 贡献2022年2月14日 (一) 12:43的版本
(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)
跳到导航 跳到搜索

本页面或章节指其读者或编者使用人称 , 我的, 我们 或者 我们的。 用encyclopedic百科全书式的语言风格进行编辑

难度等级
Difficulty 1.png
初学者

本教程重点介绍为开发自制操作系统创建所需的GCC交叉编译器。(译者注:本文中“自己的、自制的”都是说目标操作操作) 我们在这里构建的编译器有一个一般目标(i686-ELF),交叉编译器允许你的编译结果脱离当前操作系统,这意味着不会使用主机操作系统(Host OS)的引用头或库。 你 需要使用交叉编译器来进行操作系统开发,否则可能会发生很多意想不到的事情,因为一般编译器默认会假设你的代码应该在你的当前主机操作系统上运行。

导言

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

为什么需要交叉编译器

正文: Why do I need a Cross Compiler?

除非是在自制的操作系统上进行开发,否则你需要使用交叉编译器。 编译器必须知道正确的目标平台 (CPU,操作系统),否则你将遇到麻烦。 如果你使用系统附带的编译器,那么编译器将不会知道它正在完全编译其他平台的内容。 一些其它教程会建议使用你的系统已有编译器,并将许多特殊的选项传递给编译器。 这肯定会在将来给你带来很多问题,正确的解决方案应该是构建一个交叉编译器。 如果你已经尝试在不使用交叉编译器的情况下创建操作系统,请阅读文章为什么我需要交叉编译器?

选择哪个编译器版本

正文: Building GCC

推荐使用最新的GCC,因为它是最新和最好的版本。(译者注:这里指的是用什么编译器和源代码编译出新的‘交叉编译器’) 例如,使用GCC 4.6.3构建GCC 4.8.0交叉编译器可能会遇到麻烦。 如果你的系统编译器没有使用最新的主要GCC版本,我们建议你 构建最新的GCC作为你的系统编译器

你也可以使用旧版本,因为它们通常相当好。 如果你的本地系统编译器不是太旧(至少是GCC 4.6.0),你可能希望省去麻烦,只为你的交叉编译器选择非最新的版本(例如,如果你的系统编译器是4.6.1,则为选4.6.3为交叉编译器源代码版本)。

你可以通过调用以下代码查看你当前的编译器版本:

gcc --version

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

选择哪个binutils版本

正文: Cross-Compiler Successful Builds

我们建议你使用最新最好的Binutils发行版。 然而,请注意,并不是所有GCC和Binutils的组合都有效。 如果遇到麻烦,请使用与所需编译器版本大致同时发布的Binutils。 你可能至少需要Binutils 2.22,或者最好是最新的2.23.2版本。 你在当前操作系统上安装的Binutils版本并不重要。 你可以通过以下命令找到binutils版本:

ld --version

决定目标平台

正文: Target Triplet

你应该已经明确知道自己的目标平台了。 如果你正在学习Bare Bones教程,那么你应该是希望为i686 elf构建一个交叉编译器。

注意arm-none-eabi-gcc

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

为构建做准备

难度等级
Difficulty 1.png
初学者


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

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

安装依赖项

↓ Dependency / OS → 源代码 Debian (Ubuntu, Mint, WSL, ...) Gentoo Fedora Cygwin OpenBSD Arch
如何安装 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

注意:第5版。已知Texinfo的x(或更高版本)与当前的Binutils 2.23.2版本(及更早版本)不兼容。 你可以使用makeinfo--version检查当前版本。 如果你的版本太新,并且在构建过程中遇到问题,则需要使用Binutils 2.24版本 (或更新版本) 或安装旧版本的Texinfo - 也许是通过从源代码上构建 - 并在Binutils构建前后将其添加到PATH

注意: ISL的版本0.13 (或更高版本) 与当前的CLooG 0.18.1版本 (及更早版本) 不兼容。 使用ISL的0.12.2版本,否则生成肯定会失败。

下载源代码

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

注意: 使用的版本控制方案是每个句点分开一个完整的数字,即 Binutils 2.20.0比2.9.0更新。 如果查看按字母数字排序的tarball列表时,可能没有用此版本控制方案(相当常见的),这可能会让你感到困惑: 有时列表底部的文件不是最新版本! 获取最新版本的一个简单方法是按上次修改的日期排序并滚动到底部。

Linux用户构建系统编译器

你的发行版可能会发布自己补丁包的GCC和Binutils,这些修补的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交叉编译器安装到一个"slot"中,即与现有的编译器版本一起安装。 你可以通过这种方式安装多个交叉编译器,只需更改目标名称即可。 它的缺点是,它还将引入gentoo补丁,并传递与官方GCC Cross-Compiler设置不同的额外配置选项,它们的行为可能会有所不同。

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

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

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

Portage使用Overlay来存储不属于原始软件包管理的软件包。 Crossdev需要一个Overlay,在构建它们之前可以在其中存储其binutils和gcc包。 你可以正确配置一个,也可以使用PORTDIR_OVERLAY指向它应该保存其包管理器文件的位置。 使用PORTDIR_OVERLAY现有的overlay不是一个好主意,但是到那时你应该知道你是曾经是如何亲自设置它们的,以及如何正确地进行设置。 参考 [8].

MacOS用户

macOS用户需要替换libiconv,因为系统libiconv已经严重过时。 macOS用户可以通过访问网站或直接访问主FTP镜像。 否则,在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. 在OSX10.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,因为苹果从Xcode软件包中删除了它,但你仍然可以安装它。 你可以通过Homebrew或通过从源代码编译来实现,a StackExchange Answer对这两种方法都有很好的描述。

Maverick用户注意: 可以使用Xcode 5.1.1构建binutils-2.24和gcc-4.8.3(可能的其他版本)。 请注意,使用LLVM构建GCC不是官方支持的,可能会导致有趣的错误,如果你愿意冒这个风险并想节省时间构建出host-gcc只是为了编译一个cross-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的环境,如MinGWCygwin。 也许值得先研究研究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 Linux子系统(Beta版)”也是使用交叉编译器的一个选项。(使用GCC 6.1.0和Binutils 2.27于2016年8月8日进行测试)尽管处于测试状态,该交叉编译器运行速度相当快,但可能不是理想的永久性开发平台。

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 Update KB3176929修复

OpenBSD用户

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

构建

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

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

请注意,我们最好在源代码目录树之外构建所有内容。 有些软件包仅支持外部构建,有些仅支持内部构建,有些则两者兼而有之 (但可能无法对构建过程进行广泛的检查)。 在源代码目录树中构建GCC会失败得很惨,至少对于旧版本来说是如此。

准备

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

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

前缀将配置构建过程,以便你的交叉编译器环境的所有文件都以$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不要包含本机语言支持。 这基本上是可选的,但可以减少依赖性和编译时间。 这也会影响诊断结果的英文文本,当你提出问题时,论坛 上的人可能不理解。;-) (译者注:没有Get作者意思)

--with-sysroot告诉binutils在交叉编译器中启用sysroot支持,方法是将其指向默认的空目录。 默认情况下,因为没有很好的技术原因,链接器拒绝使用sysroot,而GCC能够在运行时处理这两种情况。 这将在以后有用。

GCC

另请参阅配置GCC的官方说明

现在,你可以开始构建 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的交叉编译器,则可能需要考虑构建不带“red zone”的Libgcc:Libgcc_Without_red_zone

使用新的编译器

现在你有了一个“裸”交叉编译器。 它还不能访问C库或C运行时,因此你不能使用任何标准的包含语句或创建可运行的二进制文件。 但是编译你将马上要制作的内核已经足够了。 你的工具集位于$HOME/opt/cross(或你设置的$PREFIX中)。 例如,你将GCC可执行文件安装为$HOME/opt/cross/bin/$TARGET-gcc,它将为你的目标创建程序。

你现在可以通过调用类似的东西来运行你的新编译器:

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

请注意,此编译器“无法”编译普通C程序。 每当你想要#include包含任何标准头文件时,交叉编译器都会产生错误(除了少数实际上独立于平台并由编译器本身生成的头文件)。 这是对于操作系统开发是非常必要的-你还没有目标系统的标准库!

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

要简单地通过调用 $TARGET-gcc 来使用你的新编译器,请通过键入将 $ HOME/opt/cross/bin 添加到你的 $PATH 中:

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

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

如果是你是从Bare Bones教程来到这里,那么现在你可以再使用新的交叉编译器来完成它,实现一个新的变体。 如果你开始是构建了一个新的GCC版本作为系统编译器,并使用它来构建出了交叉编译器,那么你现在可以安全地卸载那个系统编译器,除非你希望继续使用它。

故障排除

一般来说,这里要验证的仅是你仔细阅读了说明,并准确键入了命令。 不要跳过说明。 如果你使用新的shell实例,如果你没有将其添加到shell配置文件中,则必须再次设置PATH变量。 如果编译过程似乎真的搞砸了,请键入make distclean,然后重新开始make过程。 确保你的解压软件不会更改换行符。

ld: 找不到 -lgcc

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

Binutils2.9

顶部或底部的字母顺序不一定是最新版本。 2.9之后,2.10,2.11,2.12,然后有更多的版本,这些版本都是更新的,并且越来越有可能构建或支持你选择的GCC版本。

构建GCC:应该包含系统头的目录不存在

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

解决方案只需创建空文件夹:

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

这将允许构建继续进行。 发生这种情况的原因是mingw32(以及mingw本身)将包含路径和库路径配置为/mingw/INCLUDE/mingw/lib,而不是默认的/usr/INCLUDE/usr/lib。 即使这些文件夹中不需要任何东西,为什么还会构建失败,为什么不只是创建它们就可以了,我也不明白。

GCC libsanitizer无法构建

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

更进一步

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

另见

文章

外部链接

de:Cross-Compiler

预先构建的工具链

这些都是由OSdev社区中的人根据自己的构建需求构建的,并且可以随意共享,而不保证任何支持,或者它甚至可以在你的工作中直接使用。因个人而异请自行考虑使用。

Linux的最新版本(许多是ARCH的)

“适用于Linux i686主机”

对于Linux x86_64主机

下面来自phillid.tk的软件包已缩小到每对GCC和Binutils软件包约10 MiB。 请注意,这是通过只为GCC启用C前端实现的。 如果你打算用C或汇编语言以外的任何语言来编写操作系统,这些软件包不适合你。 这些实际上是Pacman包,但是将它们不加任何限制到 / 和 /rm-ing /.MTREE和包中包含的其他杂乱无章的“.”文件将工作相同。

适用于Windows主机

适用于Windows Subsystem for Linux(Beta)主机

  • 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)

适用于macOS主机

x86_64-ELFbinutilsGCC(规范目标名称x86_64-PC-ELF)可以从HOMEBREW安装:

$ brew install x86_64-elf-gcc

i686-elf工具链也在homebrew可用

针对多主机平台的ARM预构建工具链

ARM提供了它自制的基于GNU实用程序的预构建工具链,用于针对ARM系统进行开发。

Docker映像