I wanted to build a cross-compiler for MacOS X that would allow me to compile
the ELF kernel image of my tiny operating system I’m developing without
having to always use my Arch Linux distribution instead of just using OS X.
Creating a cross-compiler is not that hard, but it requires following some
instructions and it takes some time. The good thing is that once you build it,
you can leave it in a folder installed and you won’t ever have to build it
again. In this article I’ll describe how I compiled myself the
compiler on MacOS X El Capitan.
What is a cross-compiler anyway?
A cross compiler is a compiler that generates code for a different platform
than the one is running in. As an example, in this article I’ll be creating
a compiler for my Mac (
x86_64-apple-darwin15.0.0) 1, that is able to
create binaries for the
i386-elf platform. By default the clang compiler
that comes with OS X knows how to build binaries for OS X (Mach-O binaries),
but if you ask it to build an ELF binary, it will just not know what to do!
Setting up a cross-compiler is a recommended task anyway when you do
operating systems development. Yes, you can build
i386-elf binaries using
x86_64-linux GCC that comes with your Linux
distribution, but it will make a lot of assumptions about your target
platform that you’ll need to override using flags. As an example, my Arch
Linux box uses the x86-64 Linux GCC Compiler and I have to use a flag
for making it generate 32 bit code instead of 64 bit code. By using a
cross-compiler that targets just
i*86-elf, all these assumptions are
If you are using a different operating system or using a different processor architecture it might not even be possible to do that trick and setting up a cross compiler might be your only choice.
Setting up the environment
GCC has the following dependencies:
- The GCC and the G++ compiler. Since a few OS X releases, the default
compiler for OS X is actually clang 2, and running
gccactually spawns clang. It is compatible as I have had no problems building this on my machine. You might want to install the real GNU GCC from Homebrew or MacPorts.
- The libraries GNU GMP, GNU MPFR, GNU MPC. ISL and CLOOG are optional dependencies. You can choose whether to install this now (via Homebrew, MacPorts or similar) or just wait, since the GCC buildscript is able to download and compile them on the fly as I’ll show you later.
- A few other GNU dependencies such as Make, Bison, Flex and Texinfo. They probably either come preinstalled with the OS or are installed as part of the Xcode Command Line Tools, as I don’t remember installing them and Homebrew says it didn’t install them.
We will also create the folder where our compiler will live in. You
probably won’t want to install the compiler to /usr/bin or /usr/local/bin
as it might conflict with other files for your host compiler. I use
/opt/local for these kind of things, although you can use any other folder
such as $HOME/opt/ in case you don’t have enough rights on your system.
We’ll be making this folder as the $PREFIX variable to use it later
and we’ll add
$PREFIX/bin to our PATH.
Download GNU Binutils and GNU GCC. I’ll be using Binutils 2.25 and GCC 5.2.0. The trickiest part of this is that not every version of Binutils is compatible with every version of GCC. Therefore, care must be taken to use a correct version. Get the packages from the GNU FTP site and extract them in a folder such as ~/src.
Compiling GNU Binutils
Our first stop will be building binutils. It has to be done in a different folder. Make sure you provide the configure script these arguments.
Some of the arguments we provided to the configure script will make the
software be compiled without some unrequired features, such as
multiple target support (
--disable-multilib) or internationalization
You can test that the package has been successfully installed by issuing
i386-elf-as --version and checking that you get something
that makes sense.
If you are done, let’s get to building the GCC Compiler.
Compiling GNU GCC
GCC has a few dependencies. You need the gmp, mpc and mpfr libraries to compile GCC. You can get them using Homebrew or MacPorts if you wish. Alternatively, there is an script in the GCC distribution that will automatically download them for you. It has to be executed right inside the GCC folder.
It will take a few moments as it has to download a few packages and then extract them. The buildscript for GCC will detect these folders when compiling GCC and therefore will compile them on the fly so that they can be used to “compile the compiler”. Note that if you don’t do this step, you’ll have to install the libraries by yourself. Otherwise, you’ll get errors.
GCC has to be built from a different directory as well. Go ahead and compile
it using the flags described in configure. You don’t want to actually make
everything so we are just making two targets:
The first one is our compiler. The second one is the shared library that
This is going to take some time, so let’s talk about the flags while it is
were already present in the GNU Binutils build.
The most important new flag here is
--enable-languages=c,c++. GCC is
actually a compiler collection that comes with support for many programming
languages: C, C++, FORTRAN, Java, Go… we have to tell the configure script
only to create a C compiler and a C++ compiler. We don’t need any other
compiler at this point.
Once this is installed, you can test that it works as well by checking that
i386-elf-gcc command gives you something useful and not just an error
about command not found.
Testing the setup
If I tried to compile my kernel image using the standard
clang compiler, I would get some errors when linking the image:
However, if I modify the buildscript so that it uses i386-elf-gcc as the
linker (gcc also links), and I remove all the undesired flags, such as
-m32 on the compiler or
-melf_i386 on the linker (it’s an i386-elf
linker, I don’t need to tell it that), it will just work.
Does this work on other OS?
I have successfully done this on Linux as well. On Linux one has to double check that the required dependencies are installed. The building process is similar.
I want to test this both on FreeBSD and on Windows. On FreeBSD might not be hard since is a UNIX environment as well. Probably setting this on Windows requires manually installing some UNIX environment such as MSYS or Cygwin. I don’t know, I’m not that interested on Windows at the moment.
You can get this information by running
gcc -dumpmachine. Depending on the operating system or processor you are using you might get a different output. ↩
Assuming you have the Xcode Command Line tools installed, which being a developer you probably have. Although you can always run on your terminal
gcc: if you don’t, you’ll be prompted with a dialog asking whether you want to install them or not. ↩