FreeBSD on Raspberry Pi 3!

October 17, 2016 by Paweł Biernacki

On Friday, 14th October, Oleksandr Tymoshenko committed an initial support for RPI3 into FreeBSD. The system is able to boot in multiuser mode with single processor. SMP is being actively worked on. For now, only the on-board Ethernet chip is supported and we will need to wait awhile for a WiFi and Bluetooth support. The port is quite usable, and what’s more interesting - it’s full 64bit!

You can find the steps to prepare image for your RPI3 on the wiki. I made my on a late Saturday evening and my RPI3 if now happily running FreeBSD 12.0-CURRENT. Yet, I found myself with a kind of chicken and egg problem. The version of binutils included in base doesn’t have support for aarch64, and to compile anything, you need to install binutils from ports. We’re still waiting for a lld from LLVM to become usable on arm64. Therefore, the install world will not include ld, meaning that you’ll not be able to produce binaries on your RPI3.

Here are the steps to build a native, aarch64, version of pkg, binutils, and possibly a whole package repository at your will. To run FreeBSD/arm64 binaries on a amd64 system, we can use qemu-user along with binmiscctl(8). Install qemu-user-static from pkg or ports, and setup image activation for aarch64:

1
2
3
4
5
6
7
host# binmiscctl add arm64 \
    --interpreter "/usr/local/bin/qemu-aarch64-static" \
    --magic "\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\
        \x00\x00\x00\x00\x00\x00\x02\x00\xb7\x00" \
    --mask  "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\
        \xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff" \
    --size 20 --set-enabled

Create a directory and install the same version of world you used to build your RPI3 image:

1
2
host# mkdir /arm64
host# make -C /usr/src -s installworld distribution TARGET=arm64 DESTDIR=/arm64

Now we have to build binutils that can produce aarch64 code and put the in the chroot. I chose to build statically linked version of binutils for amd64 host, but aarch64 target. You have to link them statically because /libexec/ld-elf.so.1 in the chroot will not be able to run amd64 dynamically linked binaries. That’s the same reason why the qemu emulator is linked statically. Let’s use binutils and some patches for them from ports:

1
2
3
4
5
6
7
8
host# tar cxf /usr/ports/distfiles/binutils-2.27.tar.bz2
host# cd binutils-2.27/
host# for p in /usr/ports/devel/binutils/files/patch-*; do
> patch < $p
> done
[]
host# ./configure --disable-shared --target=aarch64-freebsd --disable-werror --enable-deterministic-archives --with-sysroot=/ --disable-nls --prefix=$HOME/binutils-static --build=x86_64-freebsd12.0
host# gmake LDFLAGS=-all-static all install

Then, you need to copy qemu-aarch64-static and statically linked binutils into your chroot environment:

1
2
host# cp /usr/local/bin/qemu-aarch64-static /arm64/usr/local/bin/qemu-aarch64-static
host# cp -r ~/binutils-static /arm64/usr/local/binutils-static

Mount devfs, ports, src and copy resolv.conf:

1
2
3
4
5
host# mount -t devfs devfs /arm64/dev
host# mkdir /arm64/usr/ports
host# mount_nullfs /usr/ports /arm64/usr/ports
host# mount_nullfs /usr/src /arm64/usr/src
host# cp /etc/resolv.conf /arm64/etc/resolv.conf

Now you can chroot to the arm64 environment, set prompt for aarch64, add path to static binutils and regenerate libraries cache:

1
2
3
4
host# chroot /arm64 /bin/sh
# export PS1="aarch64# "
aarch64# export PATH=$PATH:/usr/local/binutils-static/bin
aarch64# ldconfig

That’s it! You’re now in emulated environment that can build native aarch64 binaries! Let’s check it out and build pkg. Be warned that because whole qemu emulation is done in userspace it’ll be painfully slow. Like really, really slow. 10, 100 or maybe even 1000 times slower than executing native amd64 code (depending on your CPU speed):

1
2
3
aarch64# uname -srp
FreeBSD 12.0-CURRENT aarch64
aarch64# make -C /usr/ports/ports-mgmt/pkg install clean

It works for many ports, but sometimes qemu will segfault :-( usually because of internal problems with emulation of certain aarch64 instructions. However, I’ve been able to built pkg and binutils, and now have a 64bit RIP3 that can produce its own native binaries! :-)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
rpi3# cc -v -ohello hello.c
FreeBSD clang version 3.8.0 (tags/RELEASE_380/final 262564) (based on LLVM 3.8.0)
Target: aarch64-unknown-freebsd12.0
Thread model: posix
InstalledDir: /usr/bin
 "/usr/bin/cc" -cc1 -triple aarch64-unknown-freebsd12.0 -emit-obj -mrelax-all -disable-free -main-file-name hello.c -mrelocation-model static -mthread-model posix -mdisable-fp-elim -masm-verbose -mconstructor-aliases -fuse-init-array -target-cpu generic -target-feature +neon -target-abi aapcs -v -dwarf-column-info -debugger-tuning=gdb -resource-dir /usr/bin/../lib/clang/3.8.0 -fdebug-compilation-dir /root -ferror-limit 19 -fmessage-length 158 -fallow-half-arguments-and-returns -fno-signed-char -fobjc-runtime=gnustep -fdiagnostics-show-option -fcolor-diagnostics -o /tmp/hello-057377.o -x c hello.c
clang -cc1 version 3.8.0 based upon LLVM 3.8.0 default target aarch64-unknown-freebsd12.0
#include "..." search starts here:
#include <...> search starts here:
 /usr/bin/../lib/clang/3.8.0/include
 /usr/include
End of search list.
 "/usr/local/bin/ld" --eh-frame-hdr -dynamic-linker /libexec/ld-elf.so.1 --enable-new-dtags -o hello /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/crtbegin.o -L/usr/lib /tmp/hello-057377.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/crtend.o /usr/lib/crtn.o
rip3# file hello
hello: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /libexec/ld-elf.so.1, for FreeBSD 12.0 (1200013), FreeBSD-style, not stripped
rip3# ./hello
hello aarch64!
Posted in: FreeBSD