2007年12月13日木曜日

Initialize libc for Android.

This article describes how to initialize the libc for Android. At the previous article, the initialization of libc is unnecessary because the "Hello, world!" program is so simple. Normally the initialization of libc must be done before jumping to the main function.


I would like to suggest you to read the previous article at first, if you have not yet.




1. Prepare initialization routine


crt0.s:
.text
.global _start
_start:
mov r0, sp
mov r1, #0
add r2, pc, #4
add r3, pc, #4
b __libc_init
b main
.word __preinit_array_start
.word __init_array_start
.word __fini_array_start
.word __ctors_start
.word 0
.word 0

.section .preinit_array
__preinit_array_start:
.word 0xffffffff
.word 0x00000000

.section .init_array
__init_array_start:
.word 0xffffffff
.word 0x00000000

.section .fini_array
__fini_array_start:
.word 0xffffffff
.word 0x00000000

.section .ctors
__ctors_start:
.word 0xffffffff
.word 0x00000000

This part is the entry of the program. Actually, this part is common to all executables provided by Android. Except some modification, the source above have been gotten by disassembling one of executables in Android.


You see that this routine has two branches. By the first one, it jumps to _libc_init function which is provided in libc.so. By the second one, it jumps to the main function. Comparatively speaking, by the second branch, the executables provided by Android jump not to main but to the stub for arm-to-thumb interwork. In this case, we do not use thumb mode, so we directly jump to main. Note that the __libc_init is so special. It requires the four addresses at the specific position. These are the top address of the sections; .preinit_array, .init_arrays, .fini_arrays, .ctors in the order.



2. Prepare test program


hello.c:

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

int main(int argc, char **argv, char**env) {
int i;

for (i = 0; i < argc; i++) {
printf("argv[%d] = %s\n", i, argv[i]);
}
for (i = 0; env[i] != NULL; i++) {
printf("env[%d] = %s\n", i, env[i]);
}
printf("PATH = %s\n", getenv("PATH"));
setenv("HELLO", "world!", 1);
putenv("GOODBYE=cruel world!");
printf("HELLO = %s\n", getenv("HELLO"));
printf("GOODBYE = %s\n", getenv("GOODBYE"));

return 0;
}

We try to access to arguments and environment variables, now.



3. Compile and Run.


$ arm-none-linux-gnueabi-gcc -c -fno-builtin  -o crt0.o crt0.s
$ arm-none-linux-gnueabi-gcc -c -fno-builtin -o hello.o hello.c
$ arm-none-linux-gnueabi-ld \
--dynamic-linker /system/bin/linker -nostdlib \
-rpath /system/lib -rpath ~/tmp/android/system/lib \
-L ~/tmp/android/system/lib -lc -o hello3 crt0.o hello.o
$ adb push hello3 /data/hello
# ./hello3
argv[0] = ./hello3
env[0] = ANDROID_ROOT=/system
env[1] = PWD=/data/hello
env[2] = LD_LIBRARY_PATH=/system/lib
env[3] = PATH=/sbin:/system/sbin:/system/bin:/data/busybox
env[4] = DRM_CONTENT=/data/drm/content
env[5] = ANDROID_BOOTLOGO=1
env[6] = ANDROID_ASSETS=/system/app
env[7] = EXTERNAL_STORAGE=/sdcard
env[8] = ANDROID_DATA=/data
PATH = /sbin:/system/sbin:/system/bin:/data/busybox
HELLO = world!
GOODBYE = cruel world!


That's it.

1 件のコメント:

StyXman さんのコメント...

do you happen to know how to define __stack_base__? this normally is defined in crt0.o, or so it seems. I need it to link an executable against gc, the boehm's garbage collector.