Building GCC

You can build your own toolchain with GCC, Binutils, and Newlib (optional). This is not difficult but it is tedious, and it takes several minutes to compile GCC, which can make it very time-consuming to experiment with different compilation flags.

Also see: OSDev: GCC Cross-Compiler.

Overview

A working toolchain contains:

  • Binutils, which contains the linker ld, assembler as, and other tools like objdump and objcopy.
  • GCC, the compiler. This will provide certain headers like <stdint.h>, <stdarg.h>, and <stdbool.h> that define types but don’t contain library functions.
  • (Optional) A standard library, like Newlib, which contains headers like <string.h> and <stdio.h> that define library functions.

These instructions install the toolchain in /opt/n64, but you can choose any path.

ABI

These instructions build GCC for the “o32” ABI, which you get by compiling with -mabi=32. This is the old 32-bit ABI, and it’s the ABI used by LibUltra / NuSys. If you are using something other than LibUltra, you will need to build GCC with different flags.

Target Tuples

A “target tuple” identifies a system where code can run. The general format is “cpu-vendor-os”. For example, the target tuple for an x86 Linux system might be x86_64-pc-linux-gnu, and a Mac might be x86_64-apple-darwin18.7.0. For the Nintendo 64, we are going to use mips32-elf as the target tuple, which the build scripts will automatically expand to mips64-unknown-elf (MIPS CPU, unknown vendor, generic ELF OS).

The target tuple is used by the toolchain build scripts to configure settings like build architecture, ABI, the format for compiled objects, linker scripts, and macros predefined by the compiler.

Out of Tree Builds

The toolchain is not designed to be built in the source directory. In other words, you cannot run ./configure and make inside the Binutils or GCC source directory, you have to create separate folders for the build.

Preparation

Sources

Download the latest version of Binutils, GCC, and Newlib. As of November 2020, the latest versions are Binutils 2.35.1, GCC 10.2, and Newlib 3.3.0.

PATH Variable

Add /opt/n64/bin to your PATH environment variable before starting the build.

$ PATH=/opt/n64/bin:$PATH

Running Make

When you run make, pass a -j option with the number of cores your computer has. If your computer has four cores, pass -j4. This guide will use -j4 everywhere.

Prerequisites

The following software must be installed:

  • GMP
  • MPFR
  • MPC
  • GNU Sed

On a Debian/Ubuntu system, run:

$ sudo apt install build-essential libgmp-dev libmpfr-dev libmpc-dev

On macOS, using Homebrew:

$ brew install gmp mpfr libmpc gnu-sed
$ PATH=/usr/local/opt/gnu-sed/libexec/gnubin:$PATH

Building Binutils

Extract and build Binutils.

$ tar xvf binutils-2.35.1.tar.xz
$ mkdir build.binutils
$ cd build.binutils
$ ../binutils-2.35.1/configure --target=mips32-elf --prefix=/opt/n64 --with-cpu=vr4300 --disable-nls --with-sysroot=/opt/n64/mips32-elf/sysroot
$ make -j4
$ sudo make install

Building GCC, Part 1

Extract and build GCC. Don’t delete the build directory, because it will be needed later.

Check that you have GNU Sed and not some other version. If you run sed --version, it should print out “GNU sed”. Without GNU sed, the makefiles for GCC will be generated incorrectly, and you will get mysterious build errors.

$ tar xvf gcc-10.2.0.tar.xz
$ mkdir build.gcc
$ cd build.gcc
$ ../gcc-10.2.0/configure --target=mips32-elf --prefix=/opt/n64 --with-languages=c,c++ --disable-shared --disable-threads --disable-nls --without-headers --disable-multilib --with-newlib --with-sysroot=/opt/n64/mips32-elf/sysroot --with-arch=vr4300 --with-abi=32
$ make -j4 all-gcc
$ sudo make install-gcc

Building Newlib

Extract and build Newlib.

$ tar xvf newlib-3.3.0.tar.gz
$ mkdir build.newlib
$ cd build.newlib
$ ../newlib-3.3.0/configure --prefix=/usr --target=mips32-elf --disable-threads --disable-libssp CFLAGS_FOR_TARGET='-march=vr4300 -mfix4300 -G 0'
$ make -j4

The installation will not work correctly without the path set, and the sudo will ignore any changes to PATH, so you have to put the PATH inside the sudo command. Ignore this if you are installing without sudo.

$ sudo sh -c 'PATH=/opt/n64/bin:$PATH; make DESTDIR=/opt/n64/mips32-elf/sysroot install'

Newlib installs itself in not exactly the place it needs to be, so move the installed files to the correct directory:

$ cd /opt/n64/mips32-elf/sysroot/usr
$ sudo mv mips32-elf/* .
$ sudo rmdir mips32-elf

Building GCC Part 2

GCC also includes LibGCC, which must also be built. This contains low-level functions that may be needed by code you compile with GCC even if you don’t explicitly call functions.

$ cd build.gcc
$ make -j4 all-target-libgcc
$ sudo make install-target-libgcc

Done

You can now run GCC as mips32-elf-gcc.