Cross compiling the FreeBSD Kernel

Wow, two post in one day already 😉

There are two things I’d like to note. First, I noticed that cross-compiling seems a major issue for me. Don’t know why that is.

Second, I need to remember Warner Losh’s post on cross compiling FreeBSD. Essentialy, the procedure is:

$ export TARGET=powerpc
$ export TARGET_ARCH=powerpc
$ make kernel-toolchain
$ make buildkernel

Addendum: Unfortunately, this procedure works only on architectures already supported by FreeBSD and its build system. Therefore, it doesn’t work for me. So here’s the full story on how I got FreeBSD to at least compile.

Building the Toolchain

Building the toolchain is pretty straight forward. I’ve already written about how to build a cross compiler. On FreeBSD however, four things are different.

  • The target is powerpc64-unknown-freebsd. I don’t know if powerpc64-unknown-elf would have worked, too.
  • The target is new, so a patch to the binutils sources is required.
  • The GNU MP Bignum Library (GMP) is required. I used version GMP 4.2.1 and installed it in $PREFIX
  • Finally, the MPFT Library needs to be built. I used version MPFT 2.3.0 and installed it in $PREFIX

Note that those steps have to be performed before the compiler is built. Since I didn’t install the libraries in the standard locations, the LD_LIBRARY_PATH variable needs to be set before the compiler can be used:


Building the Kernel

The basic procedure of building a kernel is outlined in the FreeBSD Developer’s Handbook. Provided that the cross compiler has been installed in $PREFIX, these steps are required:

$ export MACHINE=powerpc64
$ export MACHINE_ARCH=powerpc64
$ export CC=${PREFIX}/${TARGET_PREFIX}-gcc
$ export NM=${PREFIX}/${TARGET_PREFIX}-nm
$ export LD=${PREFIX}/${TARGET_PREFIX}-ld
$ export SYSTEM_LD=${LD}
$ export OBJCOPY=${PREFIX}/${TARGET_PREFIX}-objcopy
$ cd /usr/src/sys/powerpc64/conf
$ config KERNCONF
$ cd ../compile/KERNCONF
$ make cleandepend && make depend
$ make kernel

Oh, of course this doesn’t work with the stock FreeBSD sources. Instead, my FreeBSD 64-Bit PowerPC Mercurial repository needs to be used. Note that for convenience reasons, that tree includes a crossenv script which, when sourced, sets up the required environment variables.

Cross-compiling the Linux kernel

I need to cross-compile a PowerPC Linux kernel on an x86 laptop. I’ve found instructions on how to compile (not cross-compile) the Linux kernel at this website. Further, there is a post to a mailing list here which shows how to cros-compile the kernel. The mailing list post mentions a ccontrol file, but I have no clue what that is. Luckily I’ve found this blog post, which seems to be more accurate.

Building a PowerPC Cross Compiler

I need to build my own cross compiler which will run on i386 and produce 64-Bit PowerPC Binaries. I’ve found a pretty neat introduction to building a cross compiler on IBM’s developerWorks site (registration required). The tutorial isn’t a step-by-step guide, but it helped me a lot.

The basic procedure for building a cross-compiler is:

  • Obtain headers specific to the operating system
  • Build binutils for the target platform
  • Build a bootstrap compiler
  • Build a standard C library for the target
  • Build the final cross-compiler, using the C library just built

The developerWorks tutorial doesn’t mention this, but the first three steps can easily be run in parallel. Anyways, before starting, I’ve set these environment variables:

$ export TARGET=powerpc64-unknown-linux-gnu
$ export PREFIX=/opt/crosscompiler
$ export PATH=$PATH:$PREFIX/bin

Obtaining Linux-specific Headers

I’ve followed the developerWorks tutorial on this one: Downloaded and extracting the Linux kernel sources, then copied the relevant files. Here are the commands I ran:

$ wget
$ tar xvjf linux-
$ cd linux-
$ make ARCH=powerpc CROSS_COMPILE=powerpc64-linux- menuconfig
(configure options, but tweaking isn't neccessary)
$ mkdir -p $TARGET_PREFIX/include
$ cp -r include/linux $TARGET_PREFIX/include
$ cp -r include/asm-powerpc $TARGET_PRFIX/include/asm
$ cp -r include/asm-generic $TARGET_PREFIX/include

If you read to the end of this post, then you’ll realize that this step wouldn’t be required (for now).

Building GNU binutils

I’m using GNU binutils 2.18, available from the GNU website. These are the steps required to build binutils.

$ wget 
$ tar xjvf
$ ./configure --prefix=$PREFIX --target=$TARGET --disable-nls -v
$ make all
$ make install

While building binutils did take a while, it wasn’t as long as the tutorial makes you believe. On a IBM Thinkpad T60p built around a Centrino Duo CPU running at 2.16 MHz it took less than 10 minutes. Also note the last command ("make install"), which is missing from the developerWorks tutorial.

Building a bootstrap compiler

For my project I need GCC 4.x, the latest version at the time of writing is 4.3.1 which is available from a GNU mirror near you. Downloading and extracting is easy:

$ wget
$ tar xjvf gcc-4.3.1.tar.bz2
$ cd gcc-4.3.1

Here are the steps required to build a bootstrap compiler.

$ ./configure --prefix=$PREFIX --target=$TARGET --without-headers 
  --with-newlib -v
$ make all-gcc
$ make install-gcc

This took longer than building binutils, however it took less than 30 minutes (as opposed to the hours the tutorial talks about).

Building the GNU C Library (glibc)

$ CC=${TARGET}-gcc ./configure --target=$TARGET --prefix=$PREFIX 

Unfortunately, this command failed with the following error:

checking whether __attribute__((visibility())) is supported... no
configure: error: compiler support for visibility attribute is required

However, this isn’t important as I won’t need a standard C library for now – I’m building with -ffreestanding and -nostdlib anyways. Therefore I’ve decided that I won’t pursue this futher but may come back later.

GCC 3.3 on Mac OS X

I just tried to build Qemu on an Intel Mac. Qemu needs GCC 3.x and won’t compile with GCC 4.x. The configure script for Qemu automatically detects that the GCC 3.x is installed as /usr/bin/gcc-3.3 and tries to use that. The problem is that the compiler is actually a cross-compiler for PowerPC and that it cannot produce x86 binaries. Per this post I found out that using the -arch ppc flag allows the compiler to be used on an Intel machine. Obviously the resulting binary will be a PowerPC binary, but should run under Rosetta on an Intel machine.

Note that I still don’t have a solution for building Qemu on Mac OS X. This post is just a note about one aspect of the whole problem.

The infamous memory hole

Ok, so I’ve always suspected it, i.e. had a theory, but the CPC945 manual (section 7.2) confirms it.

If a machine has 4 GBytes of memory installed, and say, 1 GByte of I/O Memory is mapped at 0x80000000 (2 GBytes) upwards, then the physical memory will still be fully accessible. It will respond to read requests in the region 0x0 thru 0x7fffffff (i.e. 0 thru 2 GBytes – 1) and to the region 0xC0000000 thru 0x140000000 (i.e. 3 GBytes thru 5 GBytes).

This will of course only work if the CPU can make requests in that range, i.e. has a large enough address bus. Hence there is an actual hole that shadows physical memory when installing 4 GBytes in a x86-based System.

The beauty of security extensions

I just spent a good day debugging a problem that eventually turned out to be (most likely) caused by some Linux security extensions deployed on the machine I test my code on.

The code loads an ELF image at runtime and then transfers control to it. Previously, I have worked with 32-Bit PowerPC executables that I ran on a 64-Bit PowerPC host. I recently changed this so that my code (as well as the ELF images it loads) would be a 64-Bit PowerPC executables.

In order to obtain memory where the ELF image could be loaded into, I previously used the malloc(3) call. I didn’t want to use mmap(2) since I was going to port the code to an environment where mmap(2) would not be available. That worked fine in the 32-Bit case.

Anyways, it turns out that, in the 64-Bit case, trying to execute code in a malloc(3)-ed buffer instantly results in a segmentation fault. Using a mmap(2)-ed buffer (with the PROT_EXEC flag) fixes the issue.

I would still like to know why there is a difference between the 32-Bit and the 64-Bit case.

Open Source SLOF

I just tried to download and build the SLOF open source release. The SLOF release itself can be downloaded here (note that an IBM ID, aka registration, is required). Let me just say that is has been a very pleasant experience – everything worked out of the box!

The release tar ball contains a file INSTALL with pretty good instructions on what needs to be done to compile the code. What’s missing is a link to the required JS20/JS21/Bimini System-Interface Code. It’s on the SLOF download page but it took me a moment to realize it’s there.

Once the system interface code has been downloaded and extracted, run the script that’s included in the tar ball. It takes one parameter, the path to the extracted SLOF source code.

Also, the x86emu code needs to be pulled from the coreboot subversion repository. Execute the script in other-licence/x86emu/ and it will do that for you.

Finally, export the CROSS environment variable. In my case I had to set it to "ppu-" by running

export CROSS=ppu-

Then, just run this command to compile the code:

make js2x

Almost all of the information above is included in the INSTALL file with the exception of the missing link. Again, this is a very pleasant surprise to me. There are other vendors where Open Source does not work so flawlessly. Hey there, Intel ;-).

Cell SDK 3.0 on IBM’s OpenClient Distribution

Today I was finally able to build a simple HelloWorld-Application with and within the TianoCore build environment. This is good news, as it leads me to the next task: Building the same HelloWorld-Application, but this time for Linux on 64-Bit PowerPC.

Since I do not yet have access to a 64-Bit PowerPC machine running Linux, I’m going to use the Cell SDK 3.0 for now. It can be used from an i386 machine and includes a toolchain as well as the full system simulator. The toolchain includes a cross-compiler that is cabable of producing binaries for the Cell BE’s PPU, which is essentially a 64-Bit PowerPC processor. The system simulator simulates a Linux-installation running on Cell.

I’m still on IBM’s OpenClient Linux distribution, which is apparently based on RHEL 5.1, at least according to /etc/redhat-release

Red Hat Enterprise Linux Client release 5.1 (Tikanga)

This is good on one hand, but made things slightly more complicated on the other hand. But first things first. Here’s what I did to prepare the Cell SDK installation:

  • I went to the developerWorks download page for the Cell SDK 3.0 and downloaded the RHEL 5.1 packages.
  • I had to download the "basic libraries and headers for cross-compiling to Cell Broadband Engine’s PPU", both the 32-Bit version and the 64-Bit version, from the Barcelona Supercomputer Center (BSC). Note that I could have built those RPMs myself, but only if I had a few other required RPMs like e.g. a glibc for PowerPC. Apparently those required RPMs are provided on the RHEL installation CDs, however, I’m on IBM’s OpenClient and thus do not have access to the installation CDs. The good thing is, the Fedora RPMs provided by the BSC turned out to work just fine.
  • For the full system simulator, I had to download the sysroot image from the BSC website.

So that’s it for the preparation part, now to actually installing the SDK.

  • I installed the installer RPM like this:
    # rpm -ivh cell-install-3.0.0-1.0.noarch.rpm

    This installs the installer to /opt/cell.

  • Now I needed to install the cross-compilation libraries and headers:
    # rpm -ivh ppu-sysroot-f7-2.noarch.rpm
    # rpm -ivh ppu-sysroot64-f7-2.noarch.rpm
  • Next I ran the installer as instructed by the installation manual:
    # cd /opt/cell
    # ./cellskd –iso /home/phs/Downloads install

After successfully running the installer, I found a functioning cross-compiler in /opt/cell/toolchain/bin.

For the system simulator, I had to install the sysroot imae RPM like this:

# rpm -ivh sysroot_image-3.0-7.noarch.rpm

Unfortunately, I wasn’t able to make the system simulator work because of a missing dependency on a simulation library.

By the way, there’s also official documentation available here.