Building for Android

From Gnash Project Wiki

Jump to: navigation, search

To start off with, Android only comes with Java based developers tools, so for us C++ projects, you have to go through the pain of getting a development environment setup. So to start, download both the Android SDK, and the Android source tree, both huge. Stick the SDK some place accessible, and then untar the sources. Android builds relatively easy, although as of March 29, 2009, you had to invoke make like this make BUILD_WITHOUT_PV=true, or you get weird errors. This appears not to be a problem a year later in March 2010.

The Android sources come a prebuilt ARM cross compiler in prebuilt/linux-x86/toolchain/, so you'd think we could use that. No such luck, while the C compiler is ok, the C++ compiler is crippled. No iostream support in libstdc++, no locales, etc...Then to make it more fun they use their own libc called Bionic, which is trimmed down for embedded devices. A bit like newlib, but is coupled to the platform much tighter.

The Code Sourcery ARM toolchains works well for C code, and has better C++ support than the NDK. I had gotten dynamic linking working with this toolchain using a custom crt0.S startup file, but static linking worked fine by redefining the entry point. As Code Sourcery works with the ARM developers, it has many bug fixes and little improvements that aren't in the standard GCC 4.4.x release. Most of these are in GCC 4.5.0, however GCC 4.5.0 is much pickier, with some older warnings now triggering errors. Fixing ones code to work with GCC 4.5.0 is recommended however, as it produces better optimized code. My current toolchain is based on a snapshnot of GCC 4.8, which flags even more warnings.

To compile with the Code Sourcery toolchain, use this example as the command line. This uses the headers that come with Code Sourcery which are from newlib. While this works for C code, for C++, we can't mix /opt/CodeSourcery_G++/bin/arm-none-eabi-gcc -Wl,--entry=main -nostdlib -L/usr/local/android-arm/sysroot/usr/lib -lc -o hello hello.c -lc -lgcc -static

or with the prebuilt tools from Google: /usr/local/src/android/prebuilt/linux-x86/toolchain/arm-eabi-4.3.1/bin/arm-eabi-gcc -nostdlib -L/usr/local/android-arm/sysroot/usr/lib -lc -o hello hello.c -lc -lgcc -static -I/usr/local/android-arm/sysroot/usr/include -Wl,--entry=main

I've built a traditional style GNU cross toolchain for Android. You can grab a snapshot of this work in progress from here with full C++ support. This is setup to work with the latest Android NDK r8. An additional file has many additional libraries is [http://www.gnashdev.org/tools/android-sysroot-20120622.tar.bz available here].

Rather than use newlib of eglibc, I choose to bootstrap ontop of the Android minimal C library,m called Bionic. The current problem with Bionic is that it has pthreads and real-time support built into libc, so there is no separate libpthread. This confuses many packages, and especially Boost to where it won't build with POSIX thread support, which Gnash needs. Boost itself is very picky about compiler versions. Versions of Boost from 1.34.0 to 1.40.0 trigger a G++ bug I've only seen when cross compiling. The bug appears as this message at link time: undefined reference to `__sync_add_and_fetch_4' collect2: ld returned 1 The current workaround for this bug is to only use Boost 1.33.1 or a newer boost, which doesn't have this problem. I tried several other versions of G++, but this bug is still in G++ subversion sources, and effects several architectures. (MIPS, SH4, ARM in my experience). Newer versions of Boost (1.40.0 or greater) have finally fixed this problem. Rather than hacking a pile of Makefiles and configure code, I extracted the object files for pthreads and rt, and made them standalone archive libraries with the usual names. This greatly simplified things, but note that at this point the code isn't truly Android compatible.

If you are using the Code Sourcery toolchain, or my older custom built one, there is also something weird with entry points in the startup code in the older SDK/NDK. I shouldn't have to specify using main, _start is the usual default, but this doesn't exist in the crt0 shipped for Android. So we force the entry point to be main, which is all _start does anyway as we don't have any command line arguments. For C++, this is trickier, as you have to initialize the constructors. Back when I used to hack on cross toolchains at Cygnus, _start called __main() instead, but that appears to have changed, so I'm tracking down what the new style is.

Well, picking this up again after about a year, the new style is called "working correctly". Part of the reason for this is Google released a Native Development Kit for Android, so I no longer have to pull all the pieces out of the SDK, which was tedious. The earlier releases of this NDK were still insufficient to support a C++ toolchain, but the current one has finally caught up. The current NDK is still crippled to not support C++ beyond the minimal WebKit needs, and the prebuilt compiler still has libstdc++ usage disabled completely. We don't care about that, as we build libstdc++ with the new toolchain, which removes this restriction.

I updated to the Android NDK r8, which had a new release, and rebuilt GCC as a cross compiler using svn (4.8 prerelease). In the past I had to use the Code Sourcery sources, which being nicely stable, were also somewhat out of date. Now Android is handled correctly as a machine target (-mandroid), specifiable on the command line. This is then processed by the GCC specfile, which control options to the various phases of the compilation and linking process to "do the right thing" now. You also need to hack the eabi config file gcc/config/eabi.h, as by default both RTTI and exceptions are disabled. The changes are pretty obvious, but here's my patch.

Getting G++ to build is now pretty easy other than one ugly hack cause I'm lazy. I used this to configure GCC:

/home/rob/projects/gnu/gcc/configure -target=arm-linux-androideabi --host=i386-linux-gnu --build=i386-linux-gnu --with-gnu-as --with-gnu-ld --enable-languages=c,c++ --without-ppl --without-cloog --disable-libssp --enable-threads --disable-nls --disable-libmudflap --disable-libgomp --disable-shared --disable-tls --disable-libitm --with-float=soft --with-fpu=vfp --enable-target-optspace --enable-initfini-array --disable-nls --prefix=/usr/local/android-toolchain --with-sysroot=/usr/local/android-toolchain/sysroot --enable-gold --with-pkgversion=Rob Savoye --disable-libquadmath --disable-plugin --enable-gold=default target_configargs=lt_cv_shlibpath_overrides_runpath=yes --enable-cxx-flags='-fexceptions -frtti'

The ugly hack is a problem caused by using Bionic instead of newlib. Deep in the configure.ac files for libiberty and libstdc++ is a test that for non-newlib targets, look for dlopen(). This of course fails with any non newlib but cross compiled C library. So you can either go through patching the configure.ac files to not do the AC_LIBTOOL_DLOPEN configure test, and regenerate the configure scripts. The other lazy hack is to just edit the configure scripts for libiberty and libstdc++ to not exit where this test fails, or if you're truly lazy, just replace exit with echo and reconfigure.

Anyway, untar this in /usr/local/, put /usr/local/android-arm/bin in your path, and invoke it as arm-eabi-g++. It's a port of GCC 4.8.0 (from svn). As a C compiler, this toolchain works great, and it's much easier to build Android executables than the other tools as it's all integrated. For C++. it does fully compile everything, but there seems to be a problem with boost. Boost is the biggest pain in the #$%^@# to cross compile successfully for a non-standard target, but I managed to beat the latest boost release into submission. Most of the boost problems were because Bionic has no wide charcacter support.

I also discovered this page the other day that basically did the same thing I did, only he modified the actual NDK files, while mine uses the regular upstream sources. But this may be also useful to people, http://www.crystax.net/android/ndk-r3.php

In late May and early June 2010, these patches for Android were checked into GCC trunk, adding Android support to the toolchain in a similar way as I did. This also works with Bionic, so pretty soon I won't be maintaining any patches at all anymore! :-) http://www.pubbs.net/201005/gcc/37284-patch-06-add-support-for-android.html

In there, he also points to a discussion with a lead Android developer about the implications of enabling RTTI and exceptions for Android. That was an issue in the older versions of Android, but has been fixed in all versions after API 9.

Building The Dependencies for Android

There are a lot of little notes about how to forcibly cross compile the libraries Gnash needs, so I moved it to it's own page at AndroidDependencies. Note that these build notes depend on using the recent version of this toolchain.

adb

adb is the remote console for the Android. Once you succeed in getting your application compiled, you use "adb push executable /data", where /data can be anywhere on the phone's filesystem. Then to run your program use adb again "adb shell /data/executable". Just running "adb shell" gives you an interactive shell on the phone.