2007年12月8日土曜日

Dynamically linked "Hello, world!" for Android

This article describes the step-by-step procedure to compile and run the dynamically linked "Hello, world!" for Android. I confirmed this procedure only on my host (Fedora 8). Sorry for Windows and Mac users.


I would like to appreciate developers who posted useful messages on Android Developers and Android Internal news groups. Especially I would like to thank to Benno who provided busybox and strace binary which work on Android.


I would like to suggest you to read the "Native C applications for Android" written by Benno before reading this article.


I hope this article is useful for all Android developers.




1. Get toolchains from CodeSourcery(Choose ARM GNU/Linux and IA32 GNU/Linux). The following description assumes that the bin directory of toolchains is added to your PATH.


2. Get strace, busybox and system image of Android from Benno's site. Copy strace and busybox to the runnning emulator. Deploy the system image on your disk. The following description assumes the system image is deployed under ~/tmp/android.


3. Prepare source files.


hello.c:

#include <stdio.h>

int main(int argc, char **argv) {
printf("Hello, world!\n");
return 0;
}

start.c:

#include <stdlib.h>
extern int main(int argc, char **argv);

void _start(int argc, char **argv)
{
exit (main (argc, argv));
}


4. Compile these source files.


$ arm-none-linux-gnueabi-gcc -c hello.c
$ arm-none-linux-gnueabi-gcc -c start.c
$ arm-none-linux-gnueabi-ld \
--entry=_start \
--dynamic-linker /system/bin/linker -nostdlib \
-rpath /system/lib -rpath ~/tmp/android/system/lib \
-L ~/tmp/android/system/lib -lc -o hello hello.o start.o


The --entry option gives the entry point of program. You can omit this option in this case because the default is _start. The --dynamic-linker option gives the place of dynamic linker. Give it from the viewpoint of the running program on Android. The -nostdlib option prevents ld from searching other library paths rather than the ones given on the command line. The first -rpath gives the place where dynamic linker search libraries. Give it from the viewpoint of the running program on Android. The second -rpath gives the place where ld search libraries needed by the library which has already loaded. Give it from the viewpoint of ld on your host.The -L option the place where ld searches the library which is given by -l option at compile time.



5. Copy binary to Android. The following description assumes that there is /data/hello directory on Android. The '#' is the prompt of Android shell.


$ adb push hello /data/hello/
# cd /data/hello
# ./hello Hello, world!


6. Check by strace whether the program ends in success.


# strace ./hello
execve("./hello", ["./hello"], [/* 9 vars */]) = 0
getpid() = 1803
syscall_983045(0xb0016b48, 0xb0013760, 0x3e4, 0, 0xbea0ae48, 0x1, 0, 0xf0005,
0xb0013760, 0, 0, 0xbea0ae44, 0, 0xbea0adf8, 0xb0000d89, 0xb00016ec, 0x10,
0xb0016b48, 0, 0, 0, 0xeb48, 0xcd1f8, 0xcd1d0, 0, 0, 0, 0, 0, 0, 0, 0) = 0
gettid() = 1803
sigaction(SIGILL, {0xb0001469, [], SA_RESTART}, {SIG_DFL}, 0) = 0
sigaction(SIGABRT, {0xb0001469, [], SA_RESTART}, {SIG_DFL}, 0) = 0
sigaction(SIGBUS, {0xb0001469, [], SA_RESTART}, {SIG_DFL}, 0) = 0
sigaction(SIGFPE, {0xb0001469, [], SA_RESTART}, {SIG_DFL}, 0) = 0
sigaction(SIGSEGV, {0xb0001469, [], SA_RESTART}, {SIG_DFL}, 0) = 0
sigaction(SIGSTKFLT, {0xb0001469, [], SA_RESTART}, {SIG_DFL}, 0) = 0
open("libc.so", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or
directory)
open("/system/lib/libc.so", O_RDONLY|O_LARGEFILE) = 3
lseek(3, -8, SEEK_END) = 231908
read(3, "\0\0\340\257PRE ", 8) = 8
mmap2(0xafe00000, 233472, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE, 3, 0) =
0xafe00000
close(3) = 0
mmap2(0xafe39000, 45056, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0,
0) = 0xafe39000
mprotect(0xafe00000, 221184, PROT_READ|PROT_EXEC) = 0
mprotect(0, 0, PROT_READ|PROT_EXEC) = 0
fstat64(1, {st_mode=S_IFREG|0666, st_size=1337, ...}) = 0
brk(0) = 0x11000
brk(0x11000) = 0x11000
brk(0x13000) = 0x13000
exit_group(0) = ?
Process 1803 detached


You see exit_group(0) at the end. The program ended in success.


7. To see memory map, modify hello.c.



#include <stdio.h>
#include <unistd.h>

int main(int argc, char **argv) {
printf("Hello, world!\n");
for(;;) sleep(1); // Zzz...
return 0;
}


This program sleeps until killed.


# ./hello &
# Hello, world!

# ps | grep hello
root 1944 450 472 184 00000000 00008318 R ./hello
# cat /proc/1944/maps
00008000-00009000 r-xp 00000000 1f:01 775 /data/hello/hello
00010000-00011000 rw-p 00000000 1f:01 775 /data/hello/hello
00011000-00013000 rwxp 00011000 00:00 0 [heap]
afe00000-afe36000 r-xp 00000000 1f:00 365 /system/lib/libc.so
afe36000-afe39000 rwxp 00036000 1f:00 365 /system/lib/libc.so
afe39000-afe44000 rw-p afe39000 00:00 0
b0000000-b0014000 rwxp 00000000 1f:00 272 /system/bin/linker
b0014000-b0019000 rwxp b0014000 00:00 0
bea6f000-bea84000 rw-p befeb000 00:00 0 [stack]

# kill 1944

10 件のコメント:

Dima さんのコメント...

Well done!

In step 1, though, you should download "ARM GNU/Linux", not "ARM EABI".

motz さんのコメント...

The selection of toolchains is mismatched between the text and the trace of execution. I corrected the text from ARM EABI to ARM GNU/Linux. Thank you, dima for pointing it out.

hiro さんのコメント...

As of Android 1.0, this scheme does not seem to work. Below is what I have learned recently.

Try changing "printf(..." to "fprintf(stdout, ...)" in motz's example, and you'll probably get nothing. This is because, under Android, the way an executable is linked to shared libraries is non-standard (or at least different from the popular glibc way). With the conventional way presented by motz, the linker (ld) allocates the executable's own copies of the library global variables such as stdin/stdout/stderr (__sF), environ, optind, etc. parallel to and unshared with the libraries.

To make an executable compatible with Android's libc (bionic), one has to force the compiler to include a few Android's special include files at the compile time and do a few other tricks at the link time. But the easiest way is to use Android's build macrons found in Android.mk's.

Overall, it's possible to drop in external executable and library modules to Android, but it's not trivial unless the Makefiles strictly follow Android's proprietary convention.

Kaiwan N Billimoria さんのコメント...

Hi hiro,

Thanks for the tips on building dynamic executables for Android. You mention:

>But the easiest way is to use Android's
>build macrons found in Android.mk's.

Could you pl elaborate on what is (and where) exactly one finds the Android.mk files and how to use them.
An example would be really useful...

TIA,
kaiwan.

hiro さんのコメント...

Hi Kaiwan,

I used system/core/toolbox/Android.mk as a reference/template for my work, which may not be the best example, though.

Here's a simplified (and unverified) Android.mk, which is scanned and run by the root-level Makefile to install /system/bin/myprog on the target.

=================================
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS) # Because all Android.mk's share a single name space

LOCAL_SRC_FILES:= x.c y.c
LOCAL_SHARED_LIBRARIES:=libc
LOCAL_MODULE:=myprog # This will create out/target/product/generic/obj/EXECUTABLES/myprog/ for *.o files

include $(BUILD_EXECUTABLE) # For creating a dynamically linked executable
=================================

I hope this will help.

Mars Alucard さんのコメント...

Hi Hiro,

Thanks for your tips on building executables for Android.

I face the same problem like what you said.

I just replaced "printf(" with "fprintf(stdout," and did all the same like what Motz did.

But I got link error message executing ~/arm-2007q3/bin/arm-none-linux-gnueabi-ld:
"
main.o: In function 'main':
main.c:(.text+0x188): undefiend reference to 'stdout'
"

Would you kindly give me some advice about, how to fix these issues?

Any your suggestions would be highly appreciated, indeed.

Mars Alucard
KLLi@cis.nctu.edu.tw

Kaiwan N Billimoria さんのコメント...

Hi Mars, Hiro,

@Mars- perhaps this link might prove useful:
http://android-dls.com/wiki/index.php?title=Compiling_for_Android

They write:
"Cross compiling native applications for the G1 can be done using the prebuilt toolchain and libraries in the Android source distribution. This can be tricky, so for simple applications, follow the instructions below."

(I haven't personally tried this out).

@Hiro- what do you think?

Mars Alucard さんのコメント...

Hi, Kaiwan & Hiro,

@Hiro-
Do you have any idea or opinion about Kaiwan's link?

@Kaiwan-
Truly thanks for your kind information, indeed.
I'll try this solution later as soon as possible.

Maybe this link also would be helpful for another Android issue I had,
which means ioctl() works on native C application, but can't work on shared library (.so) for Android application.

Thank you for the link again.

Sincerely yours,

Mars Alucard
E-Mail: KLLi@cis.nctu.edu.tw

hiro さんのコメント...

>>Mars,

>just replaced "printf(" with >"fprintf(stdout," and did all the same >like what Motz did.

>But I got link error message executing ~/arm-2007q3/bin/arm-none-linux->gnueabi-ld:
>"
>main.o: In function 'main':
>main.c:(.text+0x188): undefiend reference to 'stdout'

This is most likely you are not including BIONIC *.h files, which can be simply achieved by saying "CC=prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/arm-eabi-gcc." But you will still have the issue I mentioned (fprintf(stdout, ...) will print nothing) because BIONIC does not use versioned symbols which GCC uses by default.

In simple words, you cannot use an arbitrary tool chain you may have downloaded in its default way.

Again, use the Android.mk template I presented unless you fully understand what's going on.

hiro

Bytes さんのコメント...

Hi Hiro and others,

I'm Bytes.. learning A,B,C... on Android....

I'm using SDK 1.5 and NDK 1.1

By the by Thanks for your tip.

This is very useful for me
include $(BUILD_EXECUTABLE)

Just now I made a sample , pure [no JNI, only C] native code exe and ran on emulator, it went well with 'printf'

I don't know why documentation [ndk/docs] didn't mention this option ??? they had given only for shared/static libraries......

Any hiddent problems with native code exes... ???

However, LOCAL_SHARED_LIBRARIES:=libc

is giving a problem, its seems to be compiler is trying to build this library...

Is it assuming it also a user library ???

The documentation in NDK says that

"Note that the build system automatically links the C library, the Math
library and the C++ support library to your native code, there is no
need to list them in a LOCAL_LDLIBS line"....

hence I omitted that line...

If anyone wants to see what actually happening in the tool chain ....

Tyr at ROOT of the NDK

make V=1 APP=Your Application Name