Raspberry Pi/Minimal musl+busybox cross building
Raspberry Pi 1 (A/B/A+/B+/zero/zero w) musl/busybox environment.
Build your own minimal system using libressl, musl-libc and busybox.
This page is a work in progress and has some formatting errors and a few bits of missing information. Please contribute corrections to it if necessary. UPDATE-2018-10-20: An example Raspberry Pi 3B project based on these instructions can be found here: https://github.com/armtux/gentoo-rpi3b-radio/releases/tag/0.1-beta1
This entire process was done within an official gentoo-hardened stage3 chroot with USE="libressl", updated to ACCEPT_KEYWORDS="~amd64" (if you don't want to do this, set ~amd64 keyword individually where needed to obtain the same results from instructions below). Follow the Gentoo Handbook if you haven't already. Please read everything twice before trying - that's recommended when first installing Gentoo because it really helps! If you find mistakes below, ask for help in the #gentoo-arm or #gentoo-hardened channels on Freenode irc.
Make sure you have the following tools installed in your host system.
root #emerge -av bc layman squashfs-toolsInstall crossdev.
root #emerge -av crossdevBuild the cross-toolchain for a Raspberry Pi 1 target.
As of 2017-12-16 when building the cross-toolchain it'll complain that it can't autoconfigure CHOST for the target and errors out because of it. Temporary solution:
root #export CHOST=armv6j-hardfloat-linux-musleabiHowever, before running emerge for the host, you must unset the CHOST environment variable. for the duration of the cross-compiling process, just leave it set...
root #crossdev -t armv6j-hardfloat-linux-musleabiToolchain versions used:
- gcc-7.2.0
- linux-headers-4.13
- musl-1.1.18
- binutils-2.29.1
As of 2017-12-16, cross-musl fails, complaining about -march=native which is clearly wrong... Temporary solution: set desired target CFLAGS in the hosts's make.conf and revert to the host's cflags once crossdev completes successfully.
root #nano -w /etc/portage/make.confCFLAGS="-O2 -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s -pipe"
As of 2017-12-16, cross-gcc-stage2 fails due to execinfo.h missing.
Temporary workaround: USE="-vtv" in make.conf temporarily, because /etc/portage/package.use/cross-armv6j-hardfloat-linux-musleabi gets
overwritten every time you run the crossdev command. Then, it errors out because of an error in sanitizer functions. So USE="-sanitize" as well, but these are both temporary solutions...root #nano -w /etc/portage/make.confRe-run the crossdev command until it completes successfully...
When done, re-edit /etc/portage/make.conf and revert temporary changes.
root #nano -w /etc/portage/make.confReplace CFLAGS with proper host CFLAGS. Also, remove -vtv and -sanitize from USE variables.
Add -vtv and -sanitize to cross-gcc portage configuration; now that crossdev is finished, a world update should not overwrite the portage configuration files.
root #nano -w /etc/portage/package.use/cross-armv6j-hardfloat-linux-musleabiAdd the Gentoo musl overlay with layman.
root #layman -a muslDisable the musl overlay in host portage configuration.
root #layman -D muslAdd the musl and your local overlays to target portage configuration.
root #echo 'PORTDIR_OVERLAY="/var/lib/layman/musl /usr/local/portage"' >> /usr/armv6j-hardfloat-linux-musleabi/etc/portage/make.confEdit the cross make.conf to your liking.
root #nano -w /usr/armv6j-hardfloat-linux-musleabi/etc/portage/make.confUseful settings:
- CHOST=armv6j-hardfloat-linux-musleabi
- CFLAGS="-O2 -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s -pipe"
- USE="${ARCH} -pam libressl"
- CURL_SSL="libressl"
Set the correct cross-environment make.profile
Replace the cross-environment's make.profile with the directory right before the hardened/musl armv7a profile.
root #rm /usr/armv6j-hardfloat-linux-musleabi/etc/portage/make.profileroot #ln -s /usr/portage/profiles/default/linux/musl/arm /usr/armv6j-hardfloat-linux-musleabi/etc/portage/make.profileReplace linux-headers with raspberry pi kernel headers.
root #mkdir -p /usr/armv6j-hardfloat-linux-musleabi/usr/srcroot #cd /usr/armv6j-hardfloat-linux-musleabi/usr/srcroot #git clone --depth=1 --branch=rpi-4.14.y https://github.com/raspberrypi/linuxBackup the downloaded kernel sources.
root #cp -r linux rpi-4.14.yroot #cd linuxroot #ARCH=arm CROSS_COMPILE=armv6j-hardfloat-linux-musleabi- make headers_install INSTALL_HDR_PATH=/usr/armv6j-hardfloat-linux-musleabi/usrTime for the package salad buffet, but remember: this system is on a diet!
Check what the system would usually install if running emerge world, put it in a text file and remove the packages you don't want/need/are replacing with busybox, leaving only a very minimal, but functional list comprising of the customized stage3 install.
root #armv6j-hardfloat-linux-musleabi-emerge -ep @world > /root/armv6j-worldroot #nano -w /root/armv6j-worldroot #nano -w /usr/armv6j-hardfloat-linux-musleabi/etc/portage/profile/package.providedHere is an example of a slimmed down list of packages that were kept. It was formatted for an upcoming emerge command which causes 33 packages to install.
/root/armv6j-worldExample package list.=virtual/libintl-0-r2 =sys-apps/gentoo-functions-0.12 =virtual/libiconv-0-r2 =app-misc/mime-types-9 =virtual/shadow-0 =net-firewall/iptables-1.6.1-r2:0/12 =sys-apps/sysvinit-2.88-r9 =sys-apps/install-xattr-0.5-r1 =virtual/pager-0 =sys-apps/busybox-1.27.2 =virtual/editor-0 =sys-apps/net-tools-1.60_p20161110235919 =sys-apps/baselayout-2.4.1-r2 =virtual/os-headers-0 =dev-libs/libressl-2.6.3-r2:0/44 =sys-fs/eudev-3.2.5 =virtual/dev-manager-0 =sys-apps/iproute2-4.14.1-r2 =sys-apps/kbd-2.0.4 =virtual/modutils-0 =net-misc/iputils-20171016_pre =sys-apps/openrc-0.34.11 =net-misc/netifrc-0.6.0 =virtual/service-manager-0
/usr/armv6j-hardfloat-linux-musleabi/etc/portage/profile/package.providedHere is an example package.provided file.app-editors/nano-2.9.0 sys-apps/less-529 sys-apps/shadow-4.5 sys-process/psmisc-23.1 sys-apps/debianutils-4.8.3 sys-apps/opentmpfiles-0.1.3 sys-apps/util-linux-2.31 sys-kernel/linux-headers-4.13
Now, set use flags for packages you are going to be installing. Remember, it is meant to be tiny, and it'll all be running in the Raspberry Pi's RAM, so USE="-static make-symlinks savedconfig syslog" for busybox.
root #nano -w /usr/armv6j-hardfloat-linux-musleabi/etc/portage/package.use/world/usr/armv6j-hardfloat-linux-musleabi/etc/portage/package.use/worldHere is an example of minimizing use flags.sys-apps/openrc -ncurses sys-apps/busybox -static make-symlinks savedconfig syslog sys-apps/file -zlib sys-apps/kmod -zlib sys-fs/eudev -hwdb
Configure busybox and choose the tools wanted/needed in order to have a fully bootable/usable system. Provided at the end of this document will be known working busybox configuration examples to base oneself on.
root #emerge --fetchonly busyboxroot #mkdir -p /root/work/busyboxroot #cd /root/work/busyboxroot #tar xjf /usr/portage/distfiles/busybox-1.27.2.tar.bz2root #cd busybox-1.27.2root #make menuconfigSave the busybox .config and place it in target portage configuration.
root #mkdir -p /usr/armv6j-hardfloat-linux-musleabi/etc/portage/savedconfig/sys-appsroot #cp .config /usr/armv6j-hardfloat-linux-musleabi/etc/portage/savedconfig/sys-apps/busyboxStart by rebuilding musl-libc first, then build the base system.
{{Note|As of 2017-12-17, the musl ebuild has a hardcoded file collision test which, in this process, is to be commented out in your local overlay, which may be wrong. Possible alternative: --buildpkgonly
Look inside the if conditional for cross-* stuff and comment out [[ -e "${D}"/lib/ld-musl-${arch}.so.1 ]] || die in the src_install section of the ebuild.}}
root #mkdir -p /usr/local/portage/sys-libsroot #cp -a /usr/portage/eclass /usr/local/portage/eclassroot #cp -a /usr/portage/sys-libs/musl /usr/local/portage/sys-libs/muslroot #nano -w /usr/local/portage/sys-libs/musl/musl-1.1.18.ebuildroot #ebuild /usr/local/portage/sys-libs/musl/musl-1.1.18.ebuild digestroot #armv6j-hardfloat-linux-musleabi-emerge -av sys-libs/muslNow, emerge the packages from the minimal list. If you find errors, please make bug reports and/or see if you can find patches for your issues and submit them in your bug reports.
As of 2017-12-17, libressl fails because it can't link to musl. Solution: env/package.env stuff.
root #mkdir -p /usr/armv6j-hardfloat-linux-musleabi/etc/portage/env /usr/armv6j-hardfloat-linux-musleabi/etc/portage/package.envroot #echo 'LDFLAGS="${LDFLAGS} -L/usr/armv6j-hardfloat-linux-musleabi/usr/lib"' > /usr/armv6j-hardfloat-linux-musleabi/etc/portage/env/muslpath.confroot #echo 'dev-libs/libressl muslpath.conf' > /usr/armv6j-hardfloat-linux-musleabi/etc/portage/package.env/libresslroot #armv6j-hardfloat-linux-musleabi-emerge -av `cat /root/armv6j-world`Now wait. musl-libc is built twice for ease of use, once with crossdev and once with cross-emerge, as usually one would not be built, but once compiling has completed the binary package that this generates will be useful, along with all of the other binary packages.
Start building your squashfs root tree
Now that most of the system is built, time to emerge the extra sotware that your Raspberry Pi project will require. For the sake of simplifying these instructions, no extra packages will be installed and the instructions will skip to the next step: creating a slimmed down version of your cross-built environment using the binary packages that cross-emerge generated.
root #mkdir /usr/armv6j-hardfloat-linux-musleabi/usr/src/squashfsroot #armv6j-hardfloat-linux-musleabi-emerge --root=/usr/armv6j-hardfloat-linux-musleabi/usr/src/squashfs --config-root=/usr/armv6j-hardfloat-linux-musleabi -Kav `cat /root/armv6j-world`root #cd /usr/armv6j-hardfloat-linux-musleabi/usr/src/squashfsroot #rm -rf usr/include var/db/pkgInstall missing gcc libraries.
root #mkdir usr/lib/gccroot #cp -a /usr/lib/gcc/armv6j-hardfloat-linux-musleabi usr/lib/gcc/root #rm -rf usr/lib/gcc/*/*/include* usr/lib/gcc/*/*/plugin/include usr/lib/gcc/*/*/*.a usr/lib/gcc/*/*/*.laroot #cd libroot #ln -s ../usr/lib/gcc/*/*/*.so ./Set/unset services manually in /etc/runlevels/*
Make sure you remove hwclock from boot and add swclock. The busybox-syslogd service is also useful. So is iptables, but writing rules manually is out of scope for this document.
Here is a runlevel example.
root #cd ../etc/runlevelsroot #ln -s /etc/init.d/swclock boot/root #rm boot/hwclockCheck what services/init scripts are available.
root #ls ../init.dAs of 2017-12-17, busybox sysctl doesn't support an option used by Gentoo's sysctl init script. Solution: remove the option from the script.
root #cd ../init.droot #cp -a sysctl sysctl.oldroot #nano -w sysctlFind the sysctl --system command to read /etc/sysctl.conf and replace it with sysctl ${quiet} -p /etc/sysctl.conf
Backup the new sysctl init script just in case your change is overwritten during updates.
root #cp -a sysctl sysctl.bakHere is an example of which init scripts can be in which runlevels.
- boot: bootmisc hostname iptables keymaps localmount loopback modules mtab procfs root swclock sysctl termencoding urandom
- default: busybox-syslogd local
- nonetwork: local
- shutdown: killprocs mount-ro savecache
- sysinit: devfs dmesg kmod-static-nodes sysfs udev udev-trigger
Configure everything relevant as you would a normal Gentoo system.
You can leave /etc/fstab empty though, for now. Read the gentoo handbook and see what configuration is needed before first boot if necessary. The other Gentoo Wiki Raspberry Pi pages are useful too.
At least set a root password so you can login.
root #cp -a /etc/shadow /etc/shadow.bakroot #passwdroot #grep root /etc/shadow >> /usr/armv6j-hardfloat-linux-musleabi/usr/src/squashfs etc/shadowroot #mv /etc/shadow.bak /etc/shadowReplace the first root line with the bottom root line.
root #nano -w /usr/armv6j-hardfloat-linux-musleabi/usr/src/squashfs/etc/shadowComplete the root skeleton of needed but missing files/directories.
root #cd /usr/armv6j-hardfloat-linux-musleabi/usr/src/squashfsroot #mkdir dev home media mnt opt proc root sysroot #chmod 700 rootroot #cp -a /dev/null /dev/console /dev/tty /dev/loop0 /dev/random /dev/urandom dev/root #mknod -m 660 dev/ttyAMA0 c 204 64Configure and build the kernel.
root #cd /usr/armv6j-hardfloat-linux-musleabi/usr/src/linuxroot #unset CHOSTroot #export KERNEL=kernelroot #ARCH=arm CROSS_COMPILE=armv6j-hardfloat-linux-musleabi- make bcmrpi_defconfigroot #ARCH=arm CROSS_COMPILE=armv6j-hardfloat-linux-musleabi- make menuconfigYou probably already know what to enable/disable, and you can probably mostly leave everything as-is, but make sure that you enable tmpfs, squashfs with xz support and overlayfs for this guide, and set the default initramfs location to /usr/armv6j-hardfloat-linux-musleabi/usr/src/initramfs
Make sure you don't select xz compression for kernel or initramfs or else the kernel xz decompressor will think the xz data is corrupt and will fail to boot the kernel and halt... These instructions use gz compression instead.
root #mkdir /usr/armv6j-hardfloat-linux-musleabi/usr/src/initramfsWe have a dependency loop here. The kernel needs an initramfs, and the initramfs needs to contain kernel modules. So, first we build the kernel and install its modules into our new slimmed down/configured gentoo root.
root #ARCH=arm CROSS_COMPILE=armv6j-hardfloat-linux-musleabi- make -j4These instructions were tested with a dual-core processor with hyperthreading. Set -jN to the correct value for your processor.
root #ARCH=arm CROSS_COMPILE=armv6j-hardfloat-linux-musleabi- INSTALL_MOD_PATH=/usr/armv6j-hardfloat-linux-musleabi/usr/src/squashfs make modules_installBuilding out-of-tree drivers is out of scope for these instructions.
Build the initramfs
Take a break from the kernel to create the initramfs with a newly compiled ultra-minimal static busybox binary, the /init script that uses busybox, rudimentary device nodes and a compressed squashfs image of the /usr/armv6j-hardfloat-linux-musleabi/usr/src/squashfs directory.
root #cd /usr/armv6j-hardfloat-linux-musleabi/usr/src/initramfsroot #mkdir bin dev mnt proc sysroot #cp -a /dev/null /dev/console /dev/tty /dev/loop0 /dev/random /dev/urandom dev/root #mknod -m 660 dev/ttyAMA0 c 204 64Rebuild minimal reconfigured busybox with USE="static -syslog -make-symlinks".
root #cp /usr/armv6j-hardfloat-linux-musleabi/etc/portage/savedconfig/sys-apps/busybox /root/busybox.configroot #cd /root/work/busybox/busybox-1.27.2root #make menuconfigIt is a good idea to use the minimal busybox configuration provided below as a base.
root #cp .config ../../../busybox.mini.configroot #cp .config /usr/armv6j-hardfloat-linux-musleabi/etc/portage/savedconfig/sys-apps/busyboxroot #export CHOST=armv6j-hardfloat-linux-musleabiroot #USE="static -syslog -make-symlinks" armv6j-hardfloat-linux-musleabi-emerge -1Bav busyboxInstall minimal busybox in your initramfs.
root #armv6j-hardfloat-linux-musleabi-emerge --root=/usr/armv6j-hardfloat-linux-musleabi/usr/src/initramfs --config-root=/usr/armv6j-hardfloat-linux-musleabi -1Kav busyboxDelete unneeded files from initramfs.
root #cd /usr/armv6j-hardfloat-linux-musleabi/usr/src/initramfsroot #rm -rf etc var usr tmpMake sure you statically linked busybox.
root #file /usr/armv6j-hardfloat-linux-musleabi/usr/src/initramfs/bin/busyboxCreate a squashfs image of your minimal root for inclusion in initramfs.
root #cd /usr/armv6j-hardfloat-linux-musleabi/usr/src/squashfsroot #mksquashfs . ../initramfs/squash -comp xz -b 1048576 -Xbcj arm -Xdict-size 1048576Your squashfs image should be somewhere around 5 to 10 megabytes if you didn't install anything other than the list of packages ealier in these instructions.
Write your initramfs init script and make it executable.
root #cd ../initramfsroot #nano -w initroot #chmod 700 initA small example init script is provided below.
Regenerate an initramfs built into the kernel, including the entire OS.
root #cd ../linuxroot #rm usr/initramfs_data.cpio.gzroot #unset CHOSTroot #ARCH=arm CROSS_COMPILE=armv6j-hardfloat-linux-musleabi- make -j4Complete the installation.
Your cross-gentoo build is complete, all in only one small file! One last thing that needs doing is putting it on a FAT32 SD card partition along with the Raspberry Pi firmware.
root #mkdir /root/work/bootloaderroot #cd /root/work/bootloaderroot #git clone --depth=1 --branch=next https://github.com/raspberrypi/firmwareroot #mount /dev/SDCARD /media/sdcardroot #cd /usr/armv6j-hardfloat-linux-musleabi/usr/src/linuxroot #cp arch/arm/boot/zImage /media/sdcard/kernel.imgroot #mkdir /media/sdcard/overlaysroot #cp arch/arm/boot/dts/overlays/*.dtbo /media/sdcard/overlays/root #cp arch/arm/boot/dts/*.dtb /media/sdcard/root #cd /root/work/bootloader/firmware/bootroot #cp -r boot* fixup* start* /media/sdcard/root #umount /media/sdcardOnce you see that the system is booting, it is safe to remove the SD card from the Raspberry Pi. This setup allows for SD card swapping, because you already loaded the entire operating system into the Raspberry Pi's RAM.
Troubleshooting
System busybox config: https://gist.github.com/anonymous/d9a10a8d380a313e54f4435be15c21c1
Minimal busybox config: https://gist.github.com/anonymous/749fd5f60d0c837cb42bfd921048ac71
Example init script: https://gist.github.com/anonymous/8787dd24b6fc7e061b6637d9f61c2f8f