Operating System Development Series Part 1: Building a GCC cross compiler for i386 target on an M1 MacBook
Tags: cpp, c, systems
Published: 2022-12-21 07:12:13

A cross compiler is an important tool when building an operating system as it allows you to write code for a target system different from your host/development system. In this case I am building an operating system for an x86 architecture based device, particularly the i386 hardware which is an intel 8086 chip, from my host machine which is an M1 MacBook, the new M1 chips are ARM based which can introduce some challenges when building an operating system for a chip using a different architecture. This blog post highlights the steps I took to build the cross complier I will be using in developing the operating system.

Compilers are used to convert high level code into executable binaries on your target operating system. The compiler loads header files and dependencies of the operating system it is being compiled on. This is an issue for operating system development because we don’t want to have dependencies of our development operating system when building our own operating system as this will lead to errors.

Steps to building the cross compiler:

Building a gcc cross compiler is actually quite simple, first you need to create a folder for handling your OS development. 

In this case lets name this file OSDev

```bash
mkdir OSDev
```

This folder is where we will be keeping or Operating system development related code and tools. This helps simply our development and avoids conflicts as the compilers and tools installed might conflict with your current development system.

It is advisable to keep this OSDev folder where you keep your other software development tools, code or packages. In my case i kept it in my /Developer folder

There are two main components we are install which are:

- binutils
- gcc

The binutils are used to store the utilities your program will be able to use when it is compiled from your cross compiler, since it will not have access to your OS platform libraries.

GCC is the actual cross compiler that will be used for compiling your OS code.

```bash
# Move into the OSDev directory because this is where all your os development tools will be
cd OSDev

```

Next we will have to download the code for GCC and binutils. The easiest way to do this is by downloading the code through the mirrors of these projects.

**GETTING READY**

Before we start we have to ensure that some path parameters are exported to the shell so we can reuse it as we are building our compiler

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

The ‘PREFIX’ defines where we want our cross compiler and its dependencies to be located, while ‘TARGET’ defines what type of processor we are building for.

**BINUTILS**

You will go to the mirrors of the projects and download the .tar.gz compressed files. It is advised to download the latest version available. The link to the mirror is 

[Index of /gnu/binutils](https://ftp.gnu.org/gnu/binutils/)

When you download the compressed binutils code, extract it an move the code to the OSDev folder

```bash
cd OSDev
ls
binutils-2.39/
```

In my case the version of binutils is 2.39, this folder will be only folder in my OSDev folder at this point in time.  You can dispose the compressed file you extracted from if you wish.

Now lets build the bitutils for our target platform. First we will create a build-binutils folder which we will use for build the binaries. The build-binutils should be created in the OSDev directory.

```bash
cd OSDev
mkdir build-binutils
cd build-binutils

../binutils-2.39/configure --target=$TARGET --prefix="$PREFIX" --with-sysroot --disable-nls --disable-werror
make
make install 
```

This will build the binutils for the target platform and store it in the folder you defined as your ‘PREFIX’.

Running this commands will create an opt folder within your OSDev folder, and then a cross folder within opt

```bash
cd OSdev
ls
build-binutils/ binutils-2.39/ opt/
cd opt
ls
cross/
cd cross
ls
Lists a bunch of libraries and binaries that are now usable in you cross compiler
```

**GCC**

Now we will download the source code of the gcc compiler just as we did with the binutils. We will download the compressed .tar.gz file and extract it into our OSDev folder. We will download this from the mirror which can be found here

[https://ftp.gnu.org/gnu/gcc/](https://ftp.gnu.org/gnu/gcc/)

When you download the compressed gcc file, extract it and move the resulting folder into your OSDev folder

```bash
cd OSDev
ls
build-binutils/ binutils-2.39/ opt/ gcc-12.2.0/
cd gcc-12.2.0

# This will install all the required dependencies such as mpc, mpfr and gmp
./contrib/download_prerequisites
```

In this situation we are using the version 12.2.0 of the gcc compiler.

Now lets build the gcc compiler the same way we built the binutils.

```bash
cd OSDev

# 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-12.2.0/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
```

Congratulations! we have successfully built our compiler. The compiler is located here

```bash
OSDev/opt/cross/bin/i686-elf-gcc
```

Test the cross compiler by checking the version

```bash
OSDev/opt/cross/bin/i686-elf-gcc --version
```

Now our cross compiler is functioning properly we want to export the path so we can call the compiler from anywhere in our terminal

```bash
export PATH="full-path-to-folder/Developer/OSDev/opt/cross/bin:$PATH"
```

This will enable us to call our compiler anywhere in our terminal as such

```bash
i686-elf-gcc --version
```

This is temporary and if we close the terminal we will not be able to do this unless we re export it. To permanently export the path we need to add the export command the .profile file for the terminal. In my case i use zsh, there for i will have to edit the .zshrc file as such

```bash
nano .zshrc
```

Then add this command to the end of the file

```bash
export PATH="full-path-to-folder/Developer/OSDev/opt/cross/bin:$PATH"
```

Now you have a cross compiler and can use it to build our kernel.