Android Development in C: Difference between revisions
(Dynamically linking) |
|||
(5 intermediate revisions by the same user not shown) | |||
Line 11: | Line 11: | ||
* binutils-arm-linux-gnueabi (recommended) | * binutils-arm-linux-gnueabi (recommended) | ||
The program adb is required for communicating with the Android device. Package: android-tools-adb - Documentation can be found [http://developer.android.com/tools/help/adb.html here]. | The program <code>adb</code> is required for communicating with the Android device. Package: android-tools-adb - Documentation can be found [http://developer.android.com/tools/help/adb.html here]. | ||
=== Troubleshooting adb === | === Troubleshooting adb === | ||
Line 24: | Line 24: | ||
[http://stackoverflow.com/questions/14460656/android-debug-bridge-adb-device-no-permissions?answertab=oldest#answer-15043526 Credits] | [http://stackoverflow.com/questions/14460656/android-debug-bridge-adb-device-no-permissions?answertab=oldest#answer-15043526 Credits] | ||
=== Troubleshooting execution === | |||
==== file ==== | |||
By using the <code>file</code> 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 <code>arm-linux-gnueabi-readelf</code> or <code>arm-linux-gnueabi-objdump</code> can be used to see which libraries a program depends on ([https://stackoverflow.com/questions/6150000/cross-compiler-ldd Credits]). Prepend them with <code>LANG=C</code> 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 [https://packages.ubuntu.com/libc6-armel-cross libc6-armel-cross] they can be found in <code>/usr/arm-linux-gnueabi/lib</code>. | |||
=== Mounting /system read-write === | === Mounting /system read-write === | ||
Line 100: | Line 130: | ||
=== Customizing === | === Customizing === | ||
After extracting the files from that archive, I do a "./configure" in the directory that contains the files. This process takes about | 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 | CC = gcc -std=gnu99 | ||
Line 106: | Line 136: | ||
and replace it by | and replace it by | ||
CC = arm-linux-gnueabi-gcc - | 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: | 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: | ||
Line 112: | Line 150: | ||
qemu-arm -L /usr/arm-linux-gnueabi/ '''program''' | 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. | 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 === | === Building === | ||
Line 125: | Line 163: | ||
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 | 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 | ||
=== Problems === | |||
On my Ubuntu 18.04 system I cannot build coreutils for my Cyanogenmod device. Compiling coreutils 8.21 fails with this message: | |||
<pre> | |||
CC lib/set-mode-acl.o | |||
In file included from /usr/arm-linux-gnueabi/include/libintl.h:103:0, | |||
from lib/gettext.h:25, | |||
from lib/set-mode-acl.c:28: | |||
./lib/locale.h:50:11: fatal error: xlocale.h: No such file or directory | |||
# include <xlocale.h> | |||
^ | |||
compilation terminated. | |||
</pre> | |||
Installing package libc++-dev, which supposedly provides the file xlocale.h, did not work. | |||
Compiling coreutils 8.28 generally works (except the part of the man page creation because this requires execution of the binaries which would not work on my x64 system), but the binaries cannot be executed on my target device because its kernel is too old (2.6.32.9-FXP). | |||
[[Category:Android]] | [[Category:Android]] | ||
[[Category:Linux]] | [[Category:Linux]] | ||
[[Category:Programming]] | [[Category:Programming]] |
Latest revision as of 18:17, 4 February 2022
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
Problems
On my Ubuntu 18.04 system I cannot build coreutils for my Cyanogenmod device. Compiling coreutils 8.21 fails with this message:
CC lib/set-mode-acl.o In file included from /usr/arm-linux-gnueabi/include/libintl.h:103:0, from lib/gettext.h:25, from lib/set-mode-acl.c:28: ./lib/locale.h:50:11: fatal error: xlocale.h: No such file or directory # include <xlocale.h> ^ compilation terminated.
Installing package libc++-dev, which supposedly provides the file xlocale.h, did not work.
Compiling coreutils 8.28 generally works (except the part of the man page creation because this requires execution of the binaries which would not work on my x64 system), but the binaries cannot be executed on my target device because its kernel is too old (2.6.32.9-FXP).