If you’re writing your own kernel, you’ll need a compiler for it. The one that comes packaged with your Linux distribution may not cut it, because it’s not a baremetal or freestanding compiler; it expects a C library to be present. When you’re starting off your OS, there won’t be any C library to link to. You’ll need a baremetal compiler at this stage.

Obtaining The Compiler

There are a number of ways you can get yourself a compiler for building a kernel project, the simplest way is to find one already built for your system. However, it also isn’t too hard to build one from scratch and a number of guides exist on how to do this. The best of which is probably the OSDev Wiki guide, which worked flawlessly when I tried it today. If you’re building from scratch and assuming you want to use GCC as your compiler, you basically need the source code to the following:

  • GCC
  • Binutils
  • G++ (for building GCC)
  • GNU Make
  • Flex

There are optional things called Cloog and ISL which I left out, because they often aren’t synced up with the latest GCC. Also if you’re building on Ubuntu you’ll need to apt-get install the following packages: build-essential, bison, gawk, and texinfo.

Target Triplets

You need one compiler for each instruction set architecture (ISA) you’re going to target for your operating system. I intend to target ARM but I am building on an x86 workstation, so I want an ARM cross compiler. The procedure is the same for any architecture you target, you simply need to be aware of the target triplet of the compiler your chosen architecture.

There is something that I learned about ARM target triplets which isn’t entirely clear from most guides; some have claimed there is no difference between “EABI” and “GNUEABI”. This isn’t true. The GNUEABI option expects a GNU C library to be present, whereas EABI is the one you want for baremetal kernel building. This post by Mark Mitchell about compiling U-Boot enlightened me on the differences between the two:

U-Boot is not a GNU/Linux application. However, you’re using the
GNU/Linux toolchain to compile it — so the libraries assume the
presence of a GNU/Linux C library. In this case, the division routine
wants to call “raise” to signal a division-by-zero exception.

People often try to abuse the GNU/Linux toolchain to build U-Boot
because they want to use the same toolchain that they use to build the
Linux kernel and GNU/Linux applications. But, U-Boot is really a
bare-metal application, and, as such, should be built with a bare-metal
toolchain, like our “ARM EABI” toolchains. There are often these kinds
of problems with U-Boot when moving between different architectures or
toolchain versions because the U-Boot source code has tricks to try to
make the GNU/Linux toolchain work, and those tricks only work with
particular toolchains.

Also, when you have your cross-compiler, you probably also want a cross GDB too for debugging. When compiling GDB you need to set the target triplet to match the one for your cross-compiler. That took me a while to figure out.