Building Android 6.0 Kernel on OSX (The Missing Parts)

Come, listen to the story of our people.

Anton Vattay
Aug 3, 2016 · 9 min read

My MacBook is a wonderful daily driver and development machine. I picked up that habit working at Opower, where they were standard issue. Despite the oddities of being a BSD / Mach homunculus, it was a fine Unix environment and served well for our Ruby and Java stack.

But, times have changed. Today I am working on the Android Kernel. If you follow https://source.android.com, you can certainly build Android on OSX with relative ease. But, this only uses a prebuilt kernel. If you want to actually modify the kernel you will need this guide, which will get you about 80% there and then drop you off in the middle of broken builds-ville.

Prerequisites and Limitations

This guide assumes you already have the Android SDK installed and checked out the source for AOSP with repo. Follow the official Android guide and complete a normal Android build to make sure everything is working. If you haven’t already unlocked and rooted a phone this guide will probably just confuse you and possibly break your phone permanently.

Overview

For those new to the kernel build process, an overview of what we are about to do would be helpful. Context is great when starting something new so you aren’t just blindly copy/pasting commands.

There are many git repositories controlled by repo. Repo is a android specific meta repository of the many hundreds of git repositories that make up AOSP. There are two git repositories that we care about now, the kernel binary repository and kernel source repository. Typically, doing repo sync should automatically download the binary kernel repository for you. However, the kernel source must be downloaded manually. When you do a AOSP normal build, it just uses this pre-built kernel, this is why you can build AOSP on OSX without the kernel building headache we are about to deal with.

You will notice a lot of references to weird codenames like hammerhead and msm. Phones have codenames, and Nexus phones are typically named after sea creatures. The Nexus 5 is hammerhead and the Nexus 6 is shamu for example. See the table here for the locations of Nexus and some other kernels. Finding kernels for non Nexus devices can be it’s own adventure, you will have to track those down on the manufacturer website or a community site like http://www.xda-developers.com/.

The first thing we will do is examine the prebuilt binary kernel repo included in AOSP for our device. We will check that repo to figure out the commit hash of the kernel source repo that was used to build it.

Then, we will download the kernel source repository. We can then check out the proper commit from the last step.

You will need to verify that you have a cross-compiling toolchain from darwin to your phone’s architecture (usually arm). AOSP comes with one, so generally, you should be good to go.

Then you will build. Since we are on OSX, it will fail due to library issues. We will overcome these issues.

Next, you will need to package your kernel into a flashable boot image. The files generated by the kernel build consist of only the kernel. The initial ramdisk, the tiny in memory disk image that contains some basic drivers and executables to get things started, need to be packaged with the kernel in a very specific manner for the hardware on your phone. Because we have the AOSP repo, this is a breeze.

Finally, you will boot the image to test it, and finally flash it to make it persistent. At that point, welcome to level 0 of android kernel hacking.

Examine the Prebuilt

We will start close to the official kernel build guide, but for the Nexus 5 instead of a HiKey. It will also be from the perspective of already having the AOSP repo checked out. The official guide assumes you have not, and need to check out the kernel repo separately.

Assume your AOSP repo root is assigned to an environment variable, $AOSP. The first thing you must do, is check the pre-built kernel binary directory for your device and use git to determine the last commit from the kernel source repository which we will visit soon. Of course, if your intention is to use a different kernel as a starting point, then you are probably way over my head.

The directory with the prebuilt binary kernel is in this form<AOSP_ROOT>/device/<vendor>/<name>-kernel and should already be checked out if you did a repo sync. Name here is the codename for the device, hammerhead for the Nexus 5.

cd $AOSP/device/lge/hammerhead-kernel
git log --max-count=1

So, now you might see a commit message with something similar to:

commit 7b209ca7ce7bf98e6344c96c4ca172d6fa553549
...
Linux version 3.4.0-g0e4eb55 (android-build@vped3.mtv.corp.google.com)(gcc version 4.8 (GCC) ) #1 SMP PREEMPT...

All you need from this is the short commit hash in the message, minus the “g”. It is not the commit hash of the current commit in this repo! It is the actual commit message which contains the commit hash from the kernel source repo. In this case, 0e4eb55. Record this somewhere, we will use it a hot second. We are done with the pre-built binary.

On to the Source

Alright, now, time to get the kernel source. Refer to the device table for the proper repo. Check it out! By the way, msm stands for Qualcomm’s MSM, or Mobile Station Modem SoC.

cd $AOSP
git clone https://android.googlesource.com/kernel/msm.git

Now, before we get started, we need to have a cross compile capable gcc toolchain. Fortunately, this is available for OSX. It should already be in $AOSP/prebuilts/gcc/darwin-x86/arm/arm-eabi-4.8. This is different than the guide, which has an older version arm-eabi-4.6. So check the contents of your prebuilt/gcc/darwin-x86/arm directory. Now, you may see an arm-linux-androideabi-x.x directory in there too, but I did not use that. I believe this is for userspace, not kernel compilation.

Anyway, check your path, and if you don’t have the toolchain, do this to add it to the path and check if it was successful:

export PATH=$AOSP/prebuilts/gcc/darwin-x86/arm/arm-eabi-4.8/bin:$PATH
arm-eabi-gcc -v

You should see GCC spit out a bunch of text and list its version at the end.

And Then They Just Stopped Caring

At this point the writers of the guide probably got to a point where shit gets real and just hoped that no one would actually try this. Remember that commit they asked us to record, well, they just vaguely reference that you should use it but it’s not clear how you are supposed to use it. Furthermore, if you on OSX you are about to learn a few things about the differences between linux and darwin.

Time to Build

Alright, according to the official guide, we should be ready to go. It tells us to run something like (probably won’t work, don’t actually run this yet):

$ export ARCH=arm
$ export CROSS_COMPILE=arm-eabi
$ cd msm
$ git checkout -b android-msm-hammerhead-3.4-marshmallow-mr2 origin/android-msm-marshmallow-3.4-mr2
$ make hammerhead_defconfig
$ make

Wait, what about that commit? How did they know to checkout out android-msm-hammerhead-3.4-marshmallow-mr2? Well you can look at the git log and figure out which branch or tag you should use by educated guessing, but, nevermind that. Just use the hash from before:

git co 0e4eb55

Alright, now let’s actually try a build. In case you are wondering, this sets the target architecture to arm, which is the proper one for the Nexus 5 and most android phones. It sets the prefix for the cross compile toolchain. This is just the text prefix that will be used in front of things like gcc. In other words, all the toolchain tools will be invoked with something like arm-eabi-gcc. Then, we load up a the pre-existing kernel build configuration for the hammerhead, which loads the configuration file into a .config file in the current directory. Finally, we make, actually building the kernel, and then…

cd msm
export ARCH=arm
export CROSS_COMPILE=arm-eabi
make hammerhead_defconfig
make

You will probably have a sad face. You probably got something about elf.h missing:

... elf.h was not found ...

Off the Rails

Suddenly, OSX doesn’t seem like such a great choice! I almost capitulated after trying some advice on StackOverflow and other forums and not having success.

But there is a way! First, let’s get a nice package managed libelf. Get homebrew if you don’t have it already:

brew install libelf

Now, we have elf. I thought that this may have been as easy as just symlinking the gelf.h file from the brew libelf to /usr/local/include/elf.h, but not so fast. As I learned here, there are a whole bunch of architecture declarations missing in libelf that we need to compile… the android kernel? I’m not actually 100% sure why these are needed, would appreciate any kernel hackers who could explain why.

Anyway, we can leave the “why” for later, but the “how” is that we need a shim file that includes the gelf.h file from libelf and adds the extra declarations. Now this might also not be the most UNIX-y way to do this, so let me know if there is a better way.

By the way, these definitions were lifted from the elf headers in $AOSP/msm/arch/arm/include/asm/elf.h.

cat <<EOT >> /usr/local/include/elf.h
#include "../opt/libelf/include/libelf/gelf.h"
#define R_386_NONE 0
#define R_386_32 1
#define R_386_PC32 2
#define R_ARM_NONE 0
#define R_ARM_PC24 1
#define R_ARM_ABS32 2
#define R_MIPS_NONE 0
#define R_MIPS_16 1
#define R_MIPS_32 2
#define R_MIPS_REL32 3
#define R_MIPS_26 4
#define R_MIPS_HI16 5
#define R_MIPS_LO16 6
#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */
#define R_PPC_ADDR32 1 /* 32bit absolute address */
#define R_PPC64_ADDR64 38 /* doubleword64 S + A */
#define R_SH_DIR32 1
#define R_SPARC_64 32 /* Direct 64 bit */
#define R_X86_64_64 1 /* Direct 64 bit */
#define R_390_32 4 /* Direct 32 bit. */
#define R_390_64 22 /* Direct 64 bit. */
#define R_MIPS_64 18
EOT

Fantastic. Now in theory, your build should probably work for certain kernel versions and phones. So give it a shot! I include the ARCH and CROSS_COMPILE values here in case you didn’t export them as variables already, feel free to omit them if already defined. The -j8 just parallelizes your build over more cores for better performance, I have 8 virtual cores, use somewhere between 1–2 times the number of cores your have.

make -j8 ARCH=arm CROSS_COMPILE=arm-eabi-

If you happen to be using a Nexus 5, on the current kernel *-mr2 and have an up to date perl, you may have an error with timeconst.pl. It appears to be a linux kernel bug that is fixed in later version of the kernel. Here is a simple patch to fix that:

diff --git a/kernel/timeconst.pl b/kernel/timeconst.plindex eb51d76..0461239 100644--- a/kernel/timeconst.pl+++ b/kernel/timeconst.pl@@ -370,7 +370,7 @@ if ($hz eq '--can') {}@val = @{$canned_values{$hz}};-       if (!defined(@val)) {+       if (!@val) {@val = compute_values($hz);}output($hz, @val);

Alright, try to build again and you should have a successful build!

And then… Silence

Right, so now how do you actually get this on your phone? It has produced a bunch of files. These cannot be flashed to your phone. We need a full boot.img file that has the proper structure and incantations of the kernel and the initial ramdisk.

There are a lot of guides out there that go through pulling apart an existing boot.img to use as a basis for re-making the boot image. But, you have AOSP checked out already, don’t bother with that noise, the make system already has a target that builds the boot image!

export TARGET_PREBUILT_KERNEL=$AOSP/msm/arch/arm/boot/zImage-dtb
cd $AOSP
make bootimage

Now, just hold tight, it looks like it is about to rebuild the entire Android system again, which is a terrific way to waste several hours of your life, but it will not, it will just package the kernel in a boot.img that you can boot or flash.

Boots

Now, finally, you get to test your kernel. I figured this out from this useful guide. There are two ways to get your new boot image running on your phone, but for a new kernel, you really should only boot from it without flashing. This means that the new kernel is resident in memory but not stored. Which is great if you produce some boot looping turd because it will just fall back to the old working kernel on restart.

adb reboot bootloader
fastboot boot $AOSP/out/target/product/hammerhead/boot.img

Your phone will restart and hopefully, if you check the “About Phone” menu in the System Options, you should see your user@build-host listed.

If everything seems healthy, then you can do:

adb reboot bootloader
fastboot flash boot $AOSP/out/target/product/hammerhead/boot.img

There you go! A brand new homemade kernel. I hope someone can benefit from this, because, I really did not want to have to host another vm on my machine. I would appreciate any feedback!

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade