Guix on Android!

par Julien Lepiller — dim. 29 avril 2018

Last year I thought to myself: since my phone is just a computer running an operating system called Android (or Replicant!) , and that Android is based on a Linux kernel, it's just another foreign distribution I could install GNU Guix on, right? It turned out it was absolutely the case. Today I was reminded on IRC of my attempt last year at installing GNU Guix on my phone. Hence this blog post. I'll try to give you all the knowledge and commands required to install it on your own Android device.

Commands I give here might not work on your device. Any misuse of these commands can brick your phone, so be very careful.

Requirements

First of all, you will need an Android or Replicant device. Just like any installation of GNU Guix, you will need root access on that device. Unfortunately, in the Android world this is not very often the case by default. Then, you need a cable to connect your computer to your phone. Once the hardware is in place, you will need adb (the Android Debugging Bridge):

guix package -i adb

Exploring the device

Every Android device has its own partioning layout, but basically it works like this:

  1. A boot partition for booting the device
  2. A recovery partition for booting the device in recovery mode
  3. A data partition for user data, including applications, the user home, etc
  4. A system partition with the base system and applications. This is the place where phone companies put their own apps so you can't remove them
  5. A vendor partition for drivers
  6. Some other partitions

During the boot process, the bootloader looks for the boot partition. It doesn't contain a filesystem, but only a gzipped cpio archive (the initramfs) and the kernel. The bootloader loads them in memory and the kernel starts using the initramfs. Then, the init system from this initramfs loads partitions in their respective directories: the system partition in /system, the vendor partition in /vendor and the data partition in /data. Other partitions may be loaded.

And that's it. Android's root filesystem is actually the initramfs so any modification to its content will be lost after a reboot. Thankfully(?), Android devices are typically not rebooted often.

Another issue is the Android C library (libc), called Bionic: it has less functionality and works completely differently from the GNU libc. Since Guix is built with the Glibc, we will need to do something to make it work on our device.

Installing the necessary files

We will follow the binary installation guide. My hardware is aarch64, so I download the corresponding binary release.

Now it's time to start using adb. Connect your device and obtain root priviledges for adb. You may have to authorize root access to the computer from your phone:

adb root

Now, we will transfer some necessary files:

adb push guix-binary-* /data

# Glibc needs these two files for networking.
adb push /etc/protocols /system/etc/
adb push /etc/services /system/etc/

# … and this one to perform DNS queries.  You probably need
# to change nameservers if you use mobile data.
adb push /etc/resolv.conf /system/etc/

Note that some devices may not have /system/etc available. In that case, /etc may be available. If none is available, create the directory by using adb shell to get a shell on your device, then push the files to that new directory.

Installing Guix itself

Now all the necessary files are present on the device, so we can connect to a shell on the device:

adb shell

From that shell, we will install Guix. The root filesystem is mounted read-only as it doesn't make sense to modify it. Remember: it's a RAM filesystem. Remount-it read-write and create necessary directories:

mount -o remount,rw /
mkdir /gnu /var
mount -o remount,ro /

Now, we can't just copy the content of the binary archive to these folders because the initramfs has a limited amount of space. Guix complains when /gnu or /gnu/storeis a symlink. One solution consists in installing the content of the binary tarball on an existing partition (because you can't modify the partition layout easily) that has enough free space, typically the data partition. Then this partition is mounted on /var and /gnu.

Before that, you will need to find out what the data partition is in your system. Simply run mount | grep /data to see what partition was mounted.

We mount the partition, extract the tarball and move the contents to their final location:

mount /dev/block/bootdevice/by-name/userdata /gnu
mount /dev/block/bootdevice/by-name/userdata /var
cd /data
tar xf guix-binary-...
mv gnu/store .
mv var/guix .
rmdir gnu
rmdir var

Finally, we need to create users and groups for Guix to work properly. Since Bionic doesn't use /etc/passwd or /etc/group to store the users, we need to create them from scratch. Note the addition of the root user and group, as well as the nobody user.

# create guix users and root for glibc
cat > /etc/passwd << EOF
root:x:0:0:root:/data:/sbin/sh
nobody:x:99:99:nobody:/:/usr/bin/nologin
guixbuilder01:x:994:994:Guix build user 01:/var/empty:/usr/bin/nologin
guixbuilder02:x:993:994:Guix build user 02:/var/empty:/usr/bin/nologin
guixbuilder03:x:992:994:Guix build user 03:/var/empty:/usr/bin/nologin
guixbuilder04:x:991:994:Guix build user 04:/var/empty:/usr/bin/nologin
guixbuilder05:x:990:994:Guix build user 05:/var/empty:/usr/bin/nologin
guixbuilder06:x:989:994:Guix build user 06:/var/empty:/usr/bin/nologin
guixbuilder07:x:988:994:Guix build user 07:/var/empty:/usr/bin/nologin
guixbuilder08:x:987:994:Guix build user 08:/var/empty:/usr/bin/nologin
guixbuilder09:x:986:994:Guix build user 09:/var/empty:/usr/bin/nologin
guixbuilder10:x:985:994:Guix build user 10:/var/empty:/usr/bin/nologin
EOF

cat > /etc/group << EOF
root:x:0:root
guixbuild:x:994:guixbuilder01,guixbuilder02,guixbuilder03,guixbuilder04,guixbuilder05,guixbuilder06,guixbuilder07,guixbuilder08,guixbuilder09,guixbuilder10
EOF

Running Guix

First, we install the root profile somewhere:

export HOME=/data
ln -sf /var/guix/profiles/per-user/root/guix-profile          $HOME/.guix-profile

Now we can finally run the Guix daemon. Chrooting is impossible on my device so I had to disable it:

export PATH="$HOME/.guix-profile/bin:$HOME/.guix-profile/sbin:$PATH"
guix-daemon --build-users-group=guixbuild --disable-chroot &

To finish with, it's a good idea to allow substitutes from hydra:

mkdir /etc/guix
guix archive --authorize <   $HOME/.guix-profile/share/guix/hydra.gnu.org.pub

Enjoy!

Future work

So, now we can enjoy the Guix package manager on Android! One of the drawbacks is that after a reboot we will have to redo half of the steps: recreate /var and /gnu, mount the partitions to them. Everytime you launch a shell, you will have to export the PATH to be able to run guix. You will have to run guix-daemon manually. To solve all of these problems at once, you should modify the boot image. That's tricky and I already put some effort to it, but the phone always ends up in a boot loop after I flash a modified boot image. The nice folks at #replicant suggested that I soldered some cable to access a serial console where debug messages may be dropped. Let's see how many fingers I burn before I can boot a custom boot image!

About GNU Guix

GNU Guix is a transactional package manager for the GNU system. The Guix System Distribution or GuixSD is an advanced distribution of the GNU system that relies on GNU Guix and respects the user's freedom.

In addition to standard package management features, Guix supports transactional upgrades and roll-backs, unprivileged package management, per-user profiles, and garbage collection. Guix uses low-level mechanisms from the Nix package manager, except that packages are defined as native Guile modules, using extensions to the Scheme language. GuixSD offers a declarative approach to operating system configuration management, and is highly customizable and hackable.

GuixSD can be used on an i686, x86_64 and armv7 machines. It is also possible to use Guix on top of an already installed GNU/Linux system, including on mips64el and aarch64.