GRUB compile from source on Linux

GRUB is the acronym for GNU GRand Unified Bootloader: it is the bootloader used in practically all Linux distributions out there. Early in the boot stage, the bootloader is loaded by the machine firmware, either BIOS or UEFI (GRUB supports both of them), and it loads one of the available kernels. Being an essential software, grub is installed by default and available in the official repositories of distribution we are using; sometimes, however, we may want compile GRUB from source, either to obtain a specific version of it or to circumvent the modification the distributions could have made to the vanilla code. In this tutorial we see how to perform such operation.

In this tutorial you will learn how to:

  • Install the software needed to build grub
  • Obtain the grub source code
  • Compile GRUB and perform the grub installation on EFI and BIOS platforms
GRUB Compile from a source code
GRUB Compile from a source code

Software requirements and conventions used

Software Requirements and Linux Command Line Conventions
Category Requirements, Conventions or Software Version Used
System Distribution independent
Software See below
Other Root privileges are needed to install the software globally
Conventions # – requires given linux-commands to be executed with root privileges either directly as a root user or by use of sudo command
$ – requires given linux-commands to be executed as a regular non-privileged user

Installing GRUB compile dependencies

Before we can build grub on our system we need to install some software dependencies. The command needed to install the packages containing said software varies depending on the distribution we are using. When using Fedora, for example, we can use the dnf package manager and run:

$ sudo dnf install \
  make \
  binutils \
  bison \
  gcc \
  gettext-devel \
  flex

On Debian we can issue the following command:

$ sudo apt-get update && sudo apt-get install \
  make \
  binutils \
  bison \
  gcc \
  gettext \
  flex

On Archlinux we install packages using the pacman:

$ sudo pacman -Sy \
  make \
  diffutils \
  python \
  binutils \
  bison \
  gcc \
  gettext \
  flex

Obtaining the GRUB source code

To obtain the grub source code we can navigate with our browser to the page hosting the source code tarballs, or use a command line tool like Curl or wget to download the version we want to compile without leaving our terminal emulator. At the moment of writing the latest version of grub is 2.06. Tarballs are available with both the .xz and the .gz extensions: the source code they contain is the same, but they are compressed using different algorithms. For the sake of this example we will download the latter using curl:

$ curl -O ftp.gnu.org/gnu/grub/grub-2.06.tar.gz

We also want to download the associated .sig in order to verify the tarball signature:

$ curl -O ftp.gnu.org/gnu/grub/grub-2.06.tar.gz.sig

To verify the tarball signature with gpg we must import the public key that was used to sign the package:

$ gpg --keyserver keyserver.ubuntu.com --receive-keys BE5C23209ACDDACEB20DB0A28C8189F1988C2166

Once the key has been added to our keyring we can verify the tarball signature by running the following command:

$ gpg --verify grub-2.06.tar.gz.sig

We should receive a message of good signature like the following:

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

Compile GRUB code

We downloaded and verified the signature of the grub tarball, now, to compile the source code, the first thing we have to do is to extract its content:

$ tar -xvzf grub-2.06.tar.gz

The command above will extract the tarball content and will create a new directory called grub-2.06. At this point we want to enter it:

$ cd grub-2.06

Once inside the grub-2.06 directory we can and launch the configure script which, among the other things, is used to check the build dependencies are satisfied. The configure scripts accepts a series of options which does influence the compilation of the program: with the --prefix option, for example, we can specify where the architecture-independent files will be installed. The default value for this option is usually /usr/local (this directory is used as the installation base to avoid conflict with software installed with the distribution package manager). Sometimes we may want to change this value, for example when using stow to manage program installed from source.

Whatever prefix we will set, a grub directory will be created when we run the make install command. It will host the built binaries and libraries.

Configure GRUB compilation for a specific platform

Another important option we can use is --with-platform. This option is needed to specify for what platform the source code should be compiled. The default is guessed. To explicitly compile grub for efi, for example, we would write:

$ ./configure --with-platform=efi

A lot of other options exists and can be used to enable or disable grub features (enabling more features, could require the installation of additional build dependencies). For a detail description of them we can run:

$ ./configure -h

For the sake of this tutorial we will compile grub with the default options, so we will just run the configure script without specifying anything:

$ ./configure

If everything goes as expected, when the script will finish its job, a summary of how grub will be compiled will be printed on screen. In this case:

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

To actually compile the code, we must now use make. Optionally we can invoke it with the -j option (short for --jobs) to specify how many commands should be run simultaneously. The value usually passed to this option is the number of the available processing units (we can obtain such value by using the nproc command). If the -j option is provided without argument no limits will be imposed:

$ make -j$(nproc)

Once we run the command above the compilation will start. Once the process is complete, we can proceed with the installation. Since, as we saw, the default prefix is /usr/local, we need to launch the make install command with root privileges. In this case we will use sudo to obtain them:

$ sudo make install

Cleaning the source code directory after GRUB compile

After we compile the code, we may want to clean the source code directory from the leftovers of previous configurations, just in case we want to repeat the process. To accomplish this task we can use two make targets:

  • clean
  • distclean

What is the difference between the two? The first target causes the program binaries and objects to be removed; the latter does the same, but additionally removes also files generated by the “configure” script.

Conclusions

In this tutorial we learned how to build the grub bootloader from source. We saw how how to download the tarball containing the source code and how to verify it, how to extract the files, how to install the needed dependencies on some of the most used Linux distributions, and finally the commands needed to compile and install the software.



Comments and Discussions
Linux Forum