./blog
Running Arch Linux on Your Rooted Android Phone
23 Jul 2013

Arch Linux is my preferred distribution of GNU/Linux and I enjoy using it on all my devices. I recently discovered how to make this possible thanks to this blog post. I did things slightly differently for my current Android device, which is a Verizon branded Galaxy Nexus. I will document how I did this for my future reference and for anyone else that wanders into my neck of the internet. Make no mistake though, the author of that post deserves all the credit for making most of this process. I stress that this as much for my own reference as it is for anyone who might find this useful.

For this little howto, I expect you've:

  • rooted your phone
  • have busybox installed
  • have some terminal emulator app
  • have adb installed on your Linux computer (for which you also have root access)
  • have successfully interfaced adb with your rooted device
  • basically know your way around a Linux terminal

If any of those things have not been done, there are numerous articles online that explain it adequately. So go do that, right now, I'll wait.

Outline

The following steps can get kinda involved. At its core, the job is simple:

  1. Get the OS files for the device
  2. Make a filesystem image of those files
  3. Send said filesystem image to the device
  4. Mount the filesystem image on the device
  5. Mount pseudo-filesystems within the image's root filesystem
  6. chroot into the image's root filesystem
  7. ???  
  8. Profit

1. Downloading the OS

We'll need an OS image for your device. Most Android devices use ARM processors so we'll be using Arch Linux ARM. They've got most of the packages of the main Arch, but all ported to ARM processors. However, there is a catch. In the same way on x86 we have 32 and 64 bit variants. There are many variants of ARM. The ARM version of Arch Linux supports ARM v5, ARM v6, and ARM v7. In addition, some ARM processors support extra instructions in the same way x86 has SSE, AVX, and who knows what else. Full disclosure, I don't fully grok the difference between the ARM processors ,so let's just say the closer the version of ARM we get from Arch Linux ARM's website, the better of we'll be in terms of performance and compatibility.

Let's take my device as an example. It's a Galaxy Nexus which according to Wikipedia has a TI OMAP System on a Chip (SoC) with a ARM Cortex-A9 MPCore. Clicking on that last link tells us that that particular line of chips has multiple cores and implements the ARM v7 instruction set. Now taking a look at the list of OS archives and images that Arch Linux ARM has, we have all the information we need. The one labeled omap-smp-latest is our best bet for the Galaxy Nexus. The SMP in that label indicates Symmetric Multiprocessing.

Your device may, and most likely is, different than mine, but I've just shown you how to fish so to speak, so you'll just have to go through the process outlined above to find your device's OS. A word of warning: the following steps depend on using an OS that ends in tar.gz from the list of OS's. The ones ending in img.gz are already formatted to a specific structure for a specific device, which is mostly likely not your android device. They certainly won't work with these instructions if at all. Also, your android device may not be and ARM device, which will be apparent by reading your devices specs on Wikipedia or the manufacturer's website. In that case Arch Linux ARM is not for you. The following may be useful, but you're on your own getting an OS archive.

I didn't need to do this for my device, but it may be to go to the Arch Linux ARM home page and look through their platforms section. You might find something that matches the processor of your device. Other hardware is not as important to match because we'll use almost none (or in fact none) of the drivers that come with it. We're after the juicy packages and libraries, not the kernel.

2. Making the Filesystem Image

Linux filesystems operate exclusively on special devices called block devices that are located withing /dev. Unfortunately for us, that's no good because one cannot transfer a block device like a file. Fortunatly for us, there is a trick to make a fake block device point to a normal file on our filesystem. First thing we need is a file that will act as a block device.

$ dd if=/dev/zero of="linux.img" obs=1M seek=2048 count=0

This will create a 2GB file called linux.img of whatever happened to be on some unallocated sectors of your disk. For those unfamiliar with this command (which definitely includes me):

Argument Description
if=/dev/zero The input file for reading data is /dev/zero (a special file of limitless zeroes)
of="linux.img" The output file for writing data is linux.img
obs=1M The size of each write done be 1MB.
seek=2048 Seek 2048 times forward, each time advancing by obs sized block.
count=0 Write out 0 bytes after seeking

The neat trick is that it doesn't matter what is on the image we created because the filesystem we'll put on it assumes nothing about the data it didn't write. Not that useful to know, but cool nonetheless.

The filesystem we'll use is ext2 because every Android (and your main Linux) kernel should support it. To setup the filesystem:

$ mkfs.ext2 -F linux.img

Make sure that is executed such that linux.img is in the current directory. Now this filesystem exists, but we can't mount it. As I said before, only block devices can be mounted. Enter the magic of loop devices:

$ mknod /dev/loop256 b 7 256

This creates a block device at /dev/loop256 whose only purpose is to point to files and make them look like block devices. The exact meaning of the arguments eludes me.

$ losetup /dev/loop256 linux.img

The command losetup is what tells our loop device to point to the image of the filesystem we just made. Now mount the filesystem in the usual way.

$ mkdir linux
$ mount -t ext2 /dev/loop256 linux

I'm going to assume you're experienced enough with Linux to know or figure what the above means, but the effect is to make the filesystem inside linux.img accessible as a regular old folder called linux. We are ready to extract the OS into this folder:

$ cd linux
$ tar -xvf /path/to/arch-linux-arm.tar.gz

3. Sending the Image to the Device

DING! Your image is ready to go. Make sure your android device is connected with USB, unmount the image, and push it to the device:

$ cd ../
$ umount linux
$ adb push linux.img /sdcard/linux.img

Of course it doesn't matter how the image gets into the device, but this method is convenient and reasonably speedy.

4. Mounting the Image on the Device

Crack open your Android terminal app and gain root like so:

$ su

You may be prompted to allow your app super user permissions, which you need to allow. Mounting the image on the device is the exact same process as above:

$ cd /sdcard
$ mknod /dev/loop256 b 7 256
$ losetup /dev/loop256 linux.img
$ mkdir linux
$ mount -t ext2 /dev/loop256 linux

Now is a good time to double check that there is a root filesystem with sane folders (i.e. bin, usr, lib, etc...) within the linux directory.

5. Mounting Other Filesystems

The following makes the chrooted Linux system play nice with the real OS (Android). Replace /sdcard/linux with wherever you mounted linux.img.

$ cd /sdcard/linux
$ mount -o bind /dev dev
$ mount -o bind /dev/pts dev/pts
$ mount -o bind /sdcard mnt
$ mount -t proc none proc
$ mount -t sysfs none sys

6. chroot into the image

Now to see if all this work pays off:

$ cd ../
$ chroot linux /bin/bash

If everything is working, you should be presented with the default prompt for Arch. It's likely that there may be some glitch's due to environment variables that are inherited your Android shell session. For me, I had to set the PATH to /bin to actual execute any commands. I also got weird shell behavior until I set the TERM to something like xterm and set the HOME.

Quick tip: set the contents of the /etc/resolv.conf file to nameserver 8.8.8.8. This enables DNS lookups (through Google's DNS) within your chroot.

Outro

The rest is up to you. You can use it like any other Arch Linux system that only has text. One of the first things I would suggest is to script the process of mounting, chrooting, and setting up the shell because those get reset whenever the device or shell resets (except mounting which persists until reboot).

Extra Info

chrooted environments like ours are fascinating to me. We have a Linux system within a Linux system which grants us special advantages. Because chrooting is only changing the appearance of the filesystem to applications, system calls still go to the real (Android) kernel. This means memory management, filesystems, graphics, sound, drivers, and everything else done by the kernel is still done with the native Android kernel, which is ideal in my situation because struggling with drivers on your phone would be a nightmare.

However, the userspace of Android is absolutely hopeless. It is there to support dalvik (which runs all your apps) and not a whole lot else. The biggest place this is apparent is their use of a standard C library called bionic. This means standard Linux binaries will not work on Android even if they are targeting the right version of ARM. A recompile is necessary with the Android Native Development Kit (NDK) which is not designed for compiling standard Linux applications.

This is where installing the chrooted environment finds its use. The standard libraries are fully compatible with Android. All we had to do was make them available. With chrooting, it is easy to call any of the standard (linked against glibc) binaries and trick the linker into using our standard library. With this, all packages we get with pacman work out of the box, and anything we compile that targets the correct instruction set of ARM will run natively within the chroot.