The OSDev wiki gives an in-depth tutorial but it is not complete for a complete C++ freestanding implementation. The modifications listed under https://wiki.osdev.org/Libgcc_without_red_zone need to be followed as well to support x86_64, and some changes below are made to add the freestanding C++ headers.
The articles in the above links are detailed enough to follow, but the steps are also listed below here for posterity. The following steps were tested on Ubuntu using GCC version 11.3.0 and binutils version 2.38, the versions that come pre-installed on Ubuntu 22.04.1 LTS.
-
Create a build source directory (e.g.
$HOME/gcc-cross
) to hold the GCC source files. -
Download and extract the GCC source files corresponding to your installed GCC version (use
gcc --version
to check) to the build source directory. -
Download and extract the binutils source files corresponding to your installed binutils version (use
ld --version
to check) to the build source directory. -
Install dependencies.
Install the depndencies listed here using the following command:
sudo apt install bison flex libgmp3-dev libmpc-dev libmpfr-dev texinfo
Install the dependencies using the following command:
brew install gmp mpfr libmpc
-
Export the following environnment variables (configured to your liking)
# directory to install the compiler (under $PREFIX/bin) export PREFIX="$HOME/opt/cross" export TARGET=x86_64-elf export PATH="$PREFIX/bin:$PATH"
-
Build
binutils
:cd $HOME/gcc-cross # your build source directory mkdir build-binutils cd build-binutils ../binutils-x.y.z/configure --target=$TARGET --prefix="$PREFIX" --with-sysroot --disable-nls --disable-werror # replace x.y.z with your binutils version make make install # this may require a sudo if the install directory $PREFIX is protected
-
Run the following to check that
binutils
was properly installed from the above steps:# was the assembler built and installed at the expected path? which -- $TARGET-as || echo $TARGET-as is not in the PATH
-
Modify the GCC source files to exclude redzone from
libgcc
:- Create a file with the following contents under
gcc-x.y.z/gcc/config/i386/t-x86_64-elf
(wheregcc-x.y.z
are the downloaded GCC sources)# Add libgcc multilib variant without red-zone requirement MULTILIB_OPTIONS += mno-red-zone MULTILIB_DIRNAMES += no-red-zone
- Modify the x86_64-elf configuration located in the
gcc-x.y.z/gcc/config.gcc
file:x86_64-*-elf*) tmake_file="${tmake_file} i386/t-x86_64-elf" # <-- add this line tm_file="${tm_file} i386/unix.h i386/att.h dbxelf.h elfos.h newlib-stdint.h i386/i386elf.h i386/x86-64.h" ;;
- Create a file with the following contents under
-
Build
gcc
:cd $HOME/gcc-cross # your build source directory mkdir build-gcc cd build-gcc ../gcc-x.y.z/configure --target=$TARGET --prefix="$PREFIX" --disable-nls --enable-languages=c,c++ --without-headers --disable-hosted-libstdcxx # replace x.y.z with your binutils version make all-gcc make all-target-libgcc make all-target-libstdc++-v3 make install-gcc make install-target-libgcc make install-target-libstdc++-v3
Identical to the Linux steps, but we also need to specify where to find the installed dependencies when creating the Makefile with
configure
:../gcc-x.y.z/configure --target=$TARGET --prefix="$PREFIX" --disable-nls --enable-languages=c,c++ --without-headers --disable-hosted-libstdcxx -\ --with-gmp=/opt/homebrew/Cellar/gmp/x.y.z \ --with-mpfr=/opt/homebrew/Cellar/mpfr/x.y.z \ --with-mpc=/opt/homebrew/Cellar/libmpc/x.y.z
-
Append the following to
~/.profile
to add the built compilers and linkers to the path:export PATH="<PREFIX>/bin:$PATH"
where
<PREFIX>
is the value of the chosen install directory (the value of$PREFIX
). -
If using
clangd
, set theSTL_PATHS
in the root directory Makefile to point to the location of the compilers includes and system specific includes.
You can verify that both the C and C++ compiilers were properly built with
$PREFIX/bin/$TARGET-gcc --version
and
$PREFIX/bin/$TARGET-g++ --version
Check also that there are two versions of libgcc
(one with red-zone and one without) with
find $PREFIX/lib -name 'libgcc.a'
which should output
./gcc/x86_64-pc-elf/x.y.z/libgcc.a
./gcc/x86_64-pc-elf/x.y.z/no-red-zone/libgcc.a