Android Native Binaries without NDK

How to build native Linux binaries with Exceptions and RTTI, to run on a custom Android-based device.

The following does not describe general distributable applications.
It also assumes you know a little bit about Android, compilers, and Linux.
The need for this arose during development, and the technique described
may be suitable for enhancements and additions to the default file system
of an Android-based device.

Basically, Android is a virtual machine environment bolted on top of a linux kernel. You can communicate with that environment through the kernel, but certainly also run stand-alone applications which may have nothing to do with the Android environment. Some examples are hardware tests, communication protocols, or external C++ applications and frameworks which may require exceptions or RTTI.

Here is an outline of the steps for this proof-of-concept exercise:

Setting up for the proof of concept

My host system is Linux 10.04 LTS. I downloaded the basic Android SDK, and used that to futher install for Android 2.2, and created a default Android Virtual Device named Default_AVD. (Follow basic instructions on the Android web site, and all that should take less than 20 minutes).

Next, we want a good target compiler; CodeSourcery maintains free versions (command-line only, no Eclipse IDE) of their tool chains for ARM. This distro contains the runtime libraries already built and ready to go, compatible with the Linux kernel used in Android.

On that page are also documentation links;
the sections below are based on what I learned during
a quick review of the 'Getting Started Guide'.

I added this tools path to end of my '.bashrc' file, as:

# add path for CodeSourcery G++ Lite
PATH=/opt/CodeSourcery/Sourcery_G++_Lite/bin:$PATH
export PATH
Now, verify we can execute the compiler, and also check the sysroot for our target ARM core.
[~/]$ arm-none-linux-gnueabi-g++ -march=armv4t -print-sysroot
/opt/CodeSourcery/Sourcery_G++_Lite/bin/../arm-none-linux-gnueabi/libc/armv4t
Now, assuming you're set up in a similar way, on to the exercise:

Bundle up the target runtime libraries

We can create and run a shell script as follows.
#!/bin/bash
#
#  bundle-armv4t.sh
#
#  get the compiler sysroot path for our configuration (armv4t)
#  e.g. /opt/CodeSourcery/Sourcery_G++_Lite/arm-none-linux-gnueabi/libc/armv4t
#
THE_SYSROOT=`arm-none-linux-gnueabi-g++ -march=armv4t -print-sysroot`
export THE_SYSROOT
mkdir ~/temp
mkdir ~/temp/armv4t
cd  ~/temp/armv4t
mkdir lib
mkdir usr
mkdir usr/lib
mkdir usr/bin
mkdir sbin

# copy all required files from the sysroot for our selected target
# lib -- all .so and symlinks  (use cp -P)
# usr/lib -- all .so and symlinks  (use cp -P)
# usr/bin
# sbin

cp -P $THE_SYSROOT/lib/*.so*  ./lib/.
cp -P $THE_SYSROOT/usr/lib/*.so*  ./usr/lib/.
cp $THE_SYSROOT/usr/bin/*  ./usr/bin/.
cp $THE_SYSROOT/sbin/*  ./sbin/.

cd ..
mv armv4t sysroot
tar -cf sysroot.tar sysroot
Now we have a tarball with all symlinks at ~/temp/sysroot.tar (and we can verify).
tar -tf ~/temp/sysroot.tar

Here we will also need tar on the target. I found this available as a statically-linked (less than 1MByte) Android native binary, at http://groomws.info/index.php?title=AndroidNativeBinaries. Locate and download the "Android stripped binary" for tar. (I saved mine as 'android-tar', in the ~/Downloads folder.)

Transfer the runtime libraries to the Android-based target

We will use ADB to transfer the files, but we can use the Android console shell to make a few directories and to execute the test programs; this shell can be invoked automatically when we start the emulator.

In a separate terminal window, invoke the emulator with the '-shell' option.
(Remember, I'm using a virtual device.)

[~/android-sdk-linux_x86/tools]$  ./emulator -shell -avd Default_AVD
#
Notice the # prompt is from the Android-based target. (Try 'ls' command.)

Now, from our main terminal window, check for our virtual device. This will also automatically start the server if it's not already running.

[~/android-sdk-linux_x86/platform-tools]$  ./adb devices
List of devices attached
emulator-5554

Now we transfer the files.

[~/android-sdk-linux_x86/platform-tools]$ alias adb='./adb'
[~/android-sdk-linux_x86/platform-tools]$ adb shell mkdir /data/bin
[~/android-sdk-linux_x86/platform-tools]$ adb push ~/Downloads/android-tar /data/bin/tar
[~/android-sdk-linux_x86/platform-tools]$ adb push ~/temp/sysroot.tar  /data/sysroot.tar

in the separate terminal window (where we invoked the emulator), extract the files using the command line on the target:

# PATH=$PATH:/data/bin
# export PATH
# alias ls='ls -l'
# cd /data
# chmod 0777 ./bin/tar
# tar -xf sysroot.tar
# ls sysroot
drwxr-xr-x system   system            2011-02-27 08:22 usr
drwxr-xr-x system   system            2011-02-27 08:32 sbin
drwxr-xr-x system   system            2011-02-27 08:32 lib
#
At this point, all the shared libraries are in place, and we're ready to test a C++ program.

Test Everything

Now we build a 'Hello++' example with exceptions and RTTI, and run it from the target
[~/]$ mkdir src
[~/]$ cd src

Here's an example build script (name 'build++.sh') which contains the minimal command-line options:

#!/bin/bash

arm-none-linux-gnueabi-g++ -march=armv4t \
-Wl,-rpath=/data/sysroot/lib:/data/sysroot/usr/lib \
-Wl,--dynamic-linker=/data/sysroot/lib/ld-linux.so.3 \
-ohello++ hello.cpp

And here is the source file ('hello.cpp') to build:

#include <iostream>
#include <typeinfo>

class sample { };

int main()
{
     const char banner[] = "Hello World ++:";
     const int myexception = 13;

     std::cout << banner << std::endl ;


     sample *ptr = 0;

     std::cout << "RTTI sample name is '" << typeid(ptr).name() << "'" << std::endl ;

     try {
          throw myexception;
     }
     catch (int x) {
          std::cerr << "Caught exception (" << x << ")" << std::endl ;
     }

     return 0;
}
Now let's build it, and transfer the binary to the Android-based target.
[~/src/]$ chmod +x ./build++.sh
[~/src/]$ ./build++.sh
[~/src/]$ cd ~/android-sdk-linux_x86/platform-tools
[~/android-sdk-linux_x86/platform-tools]$ ./adb push ~/src/hello++ /data/hello++
Now that we have the executable on the target, we set the mode, and run the program.
Back in our Android console window:
# cd /data
# chmod 0777 ./hello++
# ./hello++
Hello World ++:
RTTI sample name is 'P6sample'
Caught exception (13)
#
So, that's it -- we have a full C++ runtime on the Android-based target, and we have verified it is functional.

Other Notes

Also, you're welcome to visit my home page.