Linux binaries for android
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.
- Set up Android SDK, a Virtual Device, and CodeSourcery free version
- Bundle up the target runtime libraries (provided with CodeSourcery)
- Transfer the runtime libraries to the Android-based target
- Build a simple target C++ program with exceptions and RTTI, and test it.
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).
- Go to http://www.codesourcery.com/sgpp/lite/arm/download.html
- and select the ‘GNU/Linux’ version
- from there select IA32 GNU/Linux Installer
- I installed using sudo, at location /opt/CodeSourcery/Sourcery_G++_Lite/
- You might need to adjust the shell to bash (instead of dash), per instructions.
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
[~/]$ arm-none-linux-gnueabi-g++ -march=armv4t -print-sysroot /opt/CodeSourcery/Sourcery_G++_Lite/bin/../arm-none-linux-gnueabi/libc/armv4t
Bundle up the target runtime libraries
#!/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
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 #
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 #
Test Everything
[~/]$ 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 #include class sample < >; int main() < const char banner[] = "Hello World ++:"; const int myexception = 13; std::cout catch (int x) < std::cerr return 0; >
[~/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
- In this example, the usr/lib/gconv and /usr/lib/locale files were not included; they total more than 120MBytes of files, which may not be needed for your application.
- If you already have a particular flavor of ARM compiler tools installed, it might work in a similar way. (For example, I use the Marvell flavor at work (arm-marvell-linux-gnueabi), but it still comes from CodeSourcery).
Packaging linux binary in Android apk
If I want to include a linux binary (like ffmpeg) in my .apk file (only for ARM based devices), is this possible to distribute for non rooted phones? I know it’s possible to call an included binary with Runtime.exec() but is it a recommended practice?
2 Answers 2
You can store the binary in res/raw or in assets folder. This way it will be deployed on the device. You should «extract» it after the installation, your /data/data/your.package/files/ is a valid destination; /sdcard/ is not. The usual /data/local/ destination is not accessible for your app, even if it is for an adb shell. chmod 500 is also fine.
This said, libffmpeg.so with JNI wrappers is easier to use in an Android app, and takes less system resources.
Update, Aprl 2014 You can trick the system, and store your binary in libs/armeabi folder, and let the installer extract this binary for you. It will end up in /data/data/your.package/lib/ folder, with +x permissions. This hack has another nice advantage, it lets the system choose the correct ABI to extract the binary for: armeabi, armeabi-v7a, mips, or x86.
The trick is to follow the shared library naming convention. So, when you put ffmpeg executable into libs/armeabi , rename the file to lib_ffmpeg_.so. Naturally, you will also Runtime.exec() the file using the full path, and the newly added prefix and suffix.