在 Linux 上从源代码编译 GRUB - Linux 教程
GRUB 是 GNU GRand Unified Bootloader 的缩写:它是几乎所有 Linux 发行版中使用的引导加载程序。在引导阶段的早期,引导加载程序由机器固件(BIOS 或 UEFI)加载(GRUB 支持这两者),并加载可用内核之一。作为一个重要的软件,grub 是默认安装的,并且可以在我们使用的发行版的官方存储库中找到;然而,有时我们可能希望从源代码编译 GRUB,以获得它的特定版本或规避发行版可能对原始代码进行的修改。在本教程中,我们将了解如何执行此类操作。
在本教程中,您将学习如何:
安装构建 grub 所需的软件
获取grub源代码
在EFI和BIOS平台上编译GRUB并执行grub安装
使用的软件要求和约定
安装 GRUB 编译依赖项
在我们的系统上构建 grub 之前,我们需要安装一些软件依赖项。安装包含所述软件的软件包所需的命令因我们使用的发行版而异。例如,当使用 Fedora 时,我们可以使用 dnf 包管理器并运行:
$ sudo dnf install \
make \
binutils \
bison \
gcc \
gettext-devel \
flex
在 Debian 上我们可以发出以下命令:
$ sudo apt-get update && sudo apt-get install \
make \
binutils \
bison \
gcc \
gettext \
flex
在 Archlinux 上,我们使用 pacman 安装软件包:
$ sudo pacman -Sy \
make \
diffutils \
python \
binutils \
bison \
gcc \
gettext \
flex
获取GRUB源代码
要获取 grub 源代码,我们可以使用浏览器导航到托管源代码 tarball 的页面,或者使用诸如 Curl
或 wget
之类的命令行工具来下载我们的版本想要在不离开终端模拟器的情况下进行编译。在撰写本文时,grub 的最新版本是 2.06
。 Tarball 具有 .xz
和 .gz
扩展名:它们包含的源代码相同,但使用不同的算法进行压缩。在本示例中,我们将使用 curl
下载后者:
$ curl -O ftp.gnu.org/gnu/grub/grub-2.06.tar.gz
我们还想下载关联的 .sig
以验证 tarball 签名:
$ curl -O ftp.gnu.org/gnu/grub/grub-2.06.tar.gz.sig
要使用 gpg 验证 tarball 签名,我们必须导入用于对包进行签名的公钥:
$ gpg --keyserver keyserver.ubuntu.com --receive-keys BE5C23209ACDDACEB20DB0A28C8189F1988C2166
将密钥添加到密钥环后,我们可以通过运行以下命令来验证 tarball 签名:
$ gpg --verify grub-2.06.tar.gz.sig
我们应该收到一条良好签名的消息,如下所示:
gpg: assuming signed data in 'grub-2.06.tar.gz'
gpg: Signature made Tue 08 Jun 2021 05:11:03 PM CEST
gpg: using RSA key BE5C23209ACDDACEB20DB0A28C8189F1988C2166
gpg: Good signature from "Daniel Kiper <dkiper@net-space.pl>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg: There is no indication that the signature belongs to the owner.
Primary key fingerprint: BE5C 2320 9ACD DACE B20D B0A2 8C81 89F1 988C 2166
编译 GRUB 代码
我们下载并验证了 grub tarball 的签名,现在,要编译源代码,我们要做的第一件事就是提取其内容:
$ tar -xvzf grub-2.06.tar.gz
上面的命令将提取 tarball 内容并创建一个名为 grub-2.06
的新目录。此时我们要输入:
$ cd grub-2.06
一旦进入 grub-2.06
目录,我们就可以启动 configure
脚本,该脚本除其他外,用于检查构建依赖关系是否得到满足。 configure
脚本接受一系列确实影响程序编译的选项:例如,使用 --prefix
选项,我们可以指定与体系结构无关的文件的位置将会被安装。该选项的默认值通常是/usr/local
(该目录用作安装基础,以避免与发行包管理器安装的软件发生冲突)。有时我们可能想要更改此值,例如当使用 stow 来管理从源安装的程序时。
无论我们设置什么前缀,当我们运行make install
命令时都会创建一个grub
目录。它将托管构建的二进制文件和库。
为特定平台配置 GRUB 编译
我们可以使用的另一个重要选项是--with-platform
。需要此选项来指定应为哪个平台编译源代码。默认值是猜测的。例如,要为 efi 显式编译 grub,我们可以编写:
$ ./configure --with-platform=efi
还有许多其他选项可用于启用或禁用 grub 功能(启用更多功能,可能需要安装额外的构建依赖项)。对于它们的详细描述,我们可以运行:
$ ./configure -h
为了本教程的目的,我们将使用默认选项编译 grub,因此我们将只运行配置脚本而不指定任何内容:
$ ./configure
如果一切按预期进行,当脚本完成其工作时,grub 将如何编译的摘要将打印在屏幕上。在这种情况下:
GRUB2 will be compiled with following components:
Platform: i386-pc
With devmapper support: No (need libdevmapper header)
With memory debugging: No
With disk cache statistics: No
With boot time statistics: No
efiemu runtime: Yes
grub-mkfont: No (need freetype2 library)
grub-mount: No (need FUSE library)
starfield theme: No (No build-time grub-mkfont)
With libzfs support: No (need zfs library)
Build-time grub-mkfont: No (need freetype2 library)
Without unifont (no build-time grub-mkfont)
Without liblzma (no support for XZ-compressed mips images) (need lzma library)
With stack smashing protector: No
要实际编译代码,我们现在必须使用 make
。我们可以选择使用 -j
选项(--jobs
的缩写)来调用它,以指定应同时运行多少个命令。通常传递给该选项的值是可用处理单元的数量(我们可以使用nproc
命令获取该值)。如果提供 -j
选项且不带参数,则不会施加任何限制:
$ make -j$(nproc)
一旦我们运行上面的命令,编译就会开始。一旦该过程完成,我们就可以继续安装。正如我们所看到的,由于默认前缀是 /usr/local
,因此我们需要使用 root 权限启动 make install
命令。在这种情况下,我们将使用 sudo 来获取它们:
$ sudo make install
GRUB编译后清理源代码目录
编译代码后,我们可能需要清除源代码目录中先前配置的剩余部分,以防万一我们想重复该过程。为了完成此任务,我们可以使用两个 make 目标:
干净的
距离清洁
两者有什么区别?第一个目标导致程序二进制文件和对象被删除;后者执行相同的操作,但另外还删除由“configure”脚本生成的文件。
结论
在本教程中,我们学习了如何从源代码构建 grub 引导加载程序。我们了解了如何下载包含源代码的 tarball 以及如何验证它、如何提取文件、如何在一些最常用的 Linux 发行版上安装所需的依赖项,以及最后编译和安装软件所需的命令。