Android Development in C
This article just describes how to build a simple terminal application in C and C++ that finally runs on an Android device. GUI development is not addressed.
Prerequisites
Packages required/recommended for ARM development in C
Most Android devices are equipped with a APU utilizing ARM architecture. Therefore, binaries for ARM must be created. The following packages must be used on Debian systems to install compilers for C and C++:
- gcc-arm-linux-gnueabi
- g++-arm-linux-gnueabi
- binutils-arm-linux-gnueabi (recommended)
The program adb
is required for communicating with the Android device. Package: android-tools-adb - Documentation can be found here.
Troubleshooting adb
If "adb devices" returns "???????????? no permissions", the following commands can help:
adb kill-server && adb start-server
This should be executed as root.
Troubleshooting execution
file
By using the file
command it is possible to determine if the binary was made for the target platform (ARM v7 in my case).
Example output:
base32: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.3, for GNU/Linux 3.2.0, BuildID[sha1]=0aedb66bd001b21993435dc6252feef208fe2810, with debug_info, not stripped
Used Libraries
Commands such as arm-linux-gnueabi-readelf
or arm-linux-gnueabi-objdump
can be used to see which libraries a program depends on (Credits). Prepend them with LANG=C
if your system is set to a language other than English.
arm-linux-gnueabi-readelf -a base32 | grep "Shared library:"
Example output:
0x00000001 (NEEDED) Shared library: [libc.so.6] 0x00000001 (NEEDED) Shared library: [ld-linux.so.3]
arm-linux-gnueabi-objdump -x base32 | grep NEEDED
Example output:
NEEDED libc.so.6 NEEDED ld-linux.so.3
If libraries are missing they can be copied from another system. By installing the package libc6-armel-cross they can be found in /usr/arm-linux-gnueabi/lib
.
Mounting /system read-write
That's required to write binaries to /system/bin so they can be executed. Normally, /sdcard is mounted with noexec option, hence it doesn't qualify for starting binaries from there.
mount -o rw,remount /system
Terminal emulator on Android device
A terminal emulator can be used on the device to start programs.
- Android Terminal Emulator by Jack Palevich in Google Play Store
- Android Terminal Emulator by Jack Palevich in F-Droid Repo
Hello World example in C
helloworld.c
#include <stdio.h> int main(int argc, char** argv) { printf("Hello world!\n"); return 0; }
Building and deploying
arm-linux-gnueabi-gcc helloworld.c -o helloworld
This requires the destination system to have a symbol link /lib to /system/lib. If this is not yet the case and can be done (i.e., root access is available):
mount -o rw,remount / ln -s /system/lib/ lib
Otherwise the program can be linked statically:
arm-linux-gnueabi-gcc -static helloworld.c -o helloworld
The size difference is 8040 bytes for the dynamically linked version and 589 534 bytes (576 KiB) for the statically linked version.
Finally, the binary can be stored on the destination system:
adb push helloworld /system/bin
Running
Either with adb shell /system/bin/helloworld
or by executing it on the device using a terminal emulator.
Hello World example in C++
helloworldcpp.c++
#include <iostream>
using namespace std;
int main (int argc, char **argv) { cout << "Hello World from C++!" << endl; }
Building and deploying
arm-linux-gnueabi-g++ -static helloworldcpp.c++ -o helloworldcpp adb shell /system/bin/helloworldcpp
Compiling GNU coreutils
Downloading
I am developing on Ubuntu 14.04 which ships with coreutils 8.21. coreutils have a lot of dependencies (like autoconf, autopoint, patch...), so to keep things simple, I grab the sources that match my operating system's version: coreutils_8.21.orig.tar.gz 11.7 MiB Download page
Customizing
After extracting the files from that archive, I do a "./configure" in the directory that contains the files. This process takes about 45 sec on my 6-core CPU. After that it is important to customize the "Makefile": Search for a line containing
CC = gcc -std=gnu99
and replace it by
CC = arm-linux-gnueabi-gcc CPP = arm-linux-gnueabi-g++
if the system has the "/lib" link described above. Otherwise these lines should read
CC = arm-linux-gnueabi-gcc -static CPP = arm-linux-gnueabi-g++ -static
but be aware that the binaries will have a much greater size then.
Why "-static"? Otherwise the programs get linked dynamically, so they depend on the loader to resolve linked libraries. When testing on my Ubuntu machine, it was no problem:
qemu-arm -L /usr/arm-linux-gnueabi/ program
However, on my destination device running Cyanogenmod 9, I always get a "No such file or directory" error when trying to run those files (unless "/lib" does not exist, see above).
Building
After doing a "make" - which runs about 2 min 30 sec - everything works fine. If you want to test if a program was linked statically or dynamically, use the file program:
file program
Example output:
$ file src/sleep src/sleep: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=06a81ace0e2dabd5491dea6923ce0832295ba3f8, not stripped