2015年03月16日

C4droid -- tcc と gcc

Android で動作する C コンパイラというか開発システム?に C4droid というのがある。有料なんだが、そんなアプリはこれしかないようなので、購入してみた。
add on には、「GCC for C4droid」、「SDL plugin for C4droid」あと関連アプリに「Ministro」というのがある。C4droid 自体は 5MB 程度なのだが、add on を適当にインストールしたら、SDカードに 257MB ものファイル群が出来上がった。

本当は、ちょっと tcc に興味を持っただけなのだ。/system にちょっとコンパイラを仕込んでみたいというだけなのだ。まぁ セルフの GCC も使ってみたい気はする... のだが、しょぼい IDE で使う気はしないので、どうしたものか。

とりあえず、ファイルに何があるかの調査。(以下、延々調査内容なので、読み飛ばしたほうが良いかも)

/sdcard/Android/data/com.n0n3m4.droidc/files/gcc

arm-linux-androideabi/ lib/ terminfo/
bin/ libexec/ tmpdir/
include/ plugin_version

こんな場所にそれらしきものが。

#ls bin
arm-linux-androideabi-ar arm-linux-androideabi-ranlib
arm-linux-androideabi-as arm-linux-androideabi-strip
arm-linux-androideabi-g++ m4
arm-linux-androideabi-gcc make
arm-linux-androideabi-ld pkg-config
arm-linux-androideabi-nm sdl-config
# ls arm-linux-androideabi/bin
ar as cc g++ gcc ld nm
ranlib strip

m4 と make も見つかった。ただ、objdump と objcopy がなく少々不便。

# ls -F lib/gcc/arm-linux-androideabi/4.9.1/
crtbegin.o* crtend.o* include-fixed/
crtbeginS.o* crtendS.o* libgcc.a*
crtbeginT.o* include/ libgcov.a*
# ls arm-linux-androideabi/lib/
crtbegin_dynamic.o libSDLmain.a libogg.a
crtbegin_so.o libandroid.so libpanel.a
crtbegin_static.o libc.so libpng.a
crtend_android.o libdl.so libsmpeg.a
crtend_so.o libform.a libstdc++.a
libEGL.so libfreetype.a libsupc++.a
libGLESv1_CM.so libjnigraphics.so libthread_db.so
libGLESv2.so libjpeg.a libtiff.a
libOpenSLES.so liblog.so libtiffxx.a
libSDL.a libm.so libvorbis.a
libSDL_image.a libmenu.a libvorbisfile.a
libSDL_mixer.a libnative_app_glue.a libz.so
libSDL_net.a libncurses++.a pkgconfig
libSDL_ttf.a libncurses.a

ライブラリは、.a のものと .so が混在。一体どれが使われるのだろう?
gcc は、相対パスで動作するので、ディレクトリを移動。qemu-user 環境を作って、/usr/local へ。
gcc を /usr/local/bin に置いた場合 include のパスは次になった。

"/usr/local/lib/gcc/arm-linux-androideabi/4.9.1/include" --(1)
"/usr/local/lib/gcc/arm-linux-androideabi/4.9.1/include-fixed"
"/usr/local/arm-linux-androideabi/sys-include"
"/usr/local/arm-linux-androideabi/include" --(2)

システムの include は(2) に、gcc の include は(1) に置いてみる。
次はライブラリのパス

-L/usr/local/lib/gcc/arm-linux-androideabi/4.9.1
-L/usr/local/lib/gcc

こうなっているのだが、.so がここにあっても困る。
collect2 のオプションが、

-dynamic-linker /system/bin/linker -X -m armelf_linux_eabi \
/usr/locallib/gcc/crtbegin_dynamic.o \
(xxx.o) -lgcc -lc -ldl -lgcc \
/usr/local/lib/gcc/crtend_android.o

/system/bin/linker を使うわけで、/system/lib の libc と libdl をリンクしたい。-L/system/lib 必須ということになった。

gcc hello.c -L/system/lib

これで、a.out が生成されるようにはなった。qemu-user 環境では動かないが、実機では動くというしろもの。この問題はおいておいて、次。tcc はどこにあるのか?

# ls -F /data/data/com.n0n3m4.droidc/files/
busybox* file.stderr indent* temp.c*
c4droid_version file.stdin tcc*
file.sh file.stdout temp*
# ls /sdcard/Android/data/com.n0n3m4.droidc/files/tcclibs/lib
fix.o libcrypt.a libm.a librt.a
libc.a libdl.a libpthread.a
# ls /sdcard/Android/data/com.n0n3m4.droidc/files/tcclibs/include/
a.out.h fpu_control.h netpacket stdlib.h
alloca.h getopt.h nl_types.h string.h
ar.h glob.h obstack.h strings.h
arpa gnu-versions.h paths.h sys
asm grp.h poll.h syscall.h
asm-generic ieee754.h protocols sysexits.h
assert.h internal pthread.h syslog.h
atomic.h inttypes.h pty.h tar.h
bits langinfo.h pwd.h tcclib.h
byteswap.h lastlog.h rdma termio.h
complex.h libgen.h regex.h termios.h
conio.h limits.h regexp.h tgmath.h
cpio.h link.h resolv.h thread_db.h
crypt.h linux sched.h time.h
ctype.h locale.h scsi ttyent.h
dirent.h malloc.h search.h ucontext.h
dlfcn.h math.h semaphore.h ulimit.h
elf.h memory.h setjmp.h unistd.h
endian.h mntent.h sgtty.h utime.h
err.h mqueue.h signal.h utmp.h
errno.h net stdarg.h values.h
error.h netax25 stdbool.h varargs.h
fcntl.h netdb.h stddef.h wait.h
features.h neteconet stdint.h wchar.h
float.h netinet stdio.h
fnmatch.h netipx stdio_ext.h

どうやら、ここら辺。
で、この tcc は、

tcc version 0.9.25 - Tiny C Compiler - Copyright (C) 2001-2006 Fabrice Bellard

こういうもので、バージョンがひとつ古い。参考にするに留めたい。
strings でパスらしき文字列を grep すると

/system/bin/linker
/usr/local/include
/usr/include
/usr/local/lib/tcc
/usr/local/lib
/etc/ld.so.cache
/lib:/usr/lib
/dev/zero
/etc/TZ
/etc/localtime
/proc/stat
/proc/cpuinfo
/sys/devices/system/cpu
/dev/null

こんな風になっている。/system/bin/linker が含まれているが、自分自身が ダイナミックリンクされているのだろう。他には実情に合わないパスが色々と。

まぁ、とりあえず最新版をビルドしてみよう。「TinyCC on ANDROID (ARM)」という ブログを見つけたので、知識を拝借させてもらう。
 1) sys/ucontext.h を拾ってくる。
 2) strtold() を strtod() で代用

long double strtold(const char *s, char **p) \
{ return (long double)strtod(s, p);}

 3) config.h を以下の内容で作成

#define CONFIG_TCCDIR "/system/xbin"
#define CONFIG_TCC_CRTPREFIX "/usr/local/lib:/data/local/lib:/sdcard/tcc/lib"
#define CONFIG_TCC_SYSINCLUDEPATHS \
"/usr/local/include:/data/local/include:/sdcard/tcc/include"
#define CONFIG_TCC_LIBPATHS "/system/lib:/usr/local/lib:/data/local/lib:/sdcard/tcc/lib"
#define CONFIG_TCC_ELFINTERP "/system/bin/linker"

#define HOST_ARM 1
#define TCC_ARM_VERSION 7
#define TCC_VERSION "0.9.26"

 4) tcc.c を以下の define でコンパイル

-DONE_SOURCE -DTCC_TARGET_ARM -DTCC_ARM_EABI -DWITHOUT_LIBTCC

うそでしょうと思うほどあっさりとビルドできた。
やっぱり qemu-user 環境で動かないので、実機で。
tcclib.h stdarg.h stddef.h
だけ用意して 実験

# ./tccwk
tcc version 0.9.26 - Tiny C Compiler - Copyright (C) 2001-2006 Fabrice Bellard
Usage: tcc [options...] [-o outfile] [-c] infile(s)...
tcc [options...] -run infile [arguments...]
General options:
(略)
# ./tccwk -I. -c hello.c
./tccwk -I. hello.c
tcc: error: file 'crt1.o' not found
tcc: error: file 'crti.o' not found
tcc: error: file 'crtn.o' not found

おしい。gcc で使った、crt を用意して

./tccwk -nostdlib -I. ./crtbegin_dynamic.o hello.c crtend_android.o -L/system/lib -lc -ldl
./a.out
hello
./tccwk -nostdlib -I. crtbegin_dynamic.o hello.c crtend_android.o -L/system/lib -lc -ldl -run
hello

えー、こんなにあっさりと動くものなのカ。ブログ主に感謝。

では、tcc 自身をビルドしてみよう。
... と include ファイルをどうするのか?という問題があった。
出来るだけコンパクトにしたいので、使っているものを調べてみる。

dlfcn.h fcntl.h math.h signal.h stdio.h
string.h unistd.h
errno.h inttypes.h setjmp.h stdarg.h stdlib.h
time.h
sys/mman.h sys/time.h sys/ucontext.h


まぁこんなものか。... なんてのは甘かった。include がネストして

tcclib/include/:
alloca.h fcntl.h math.h stddef.h sys/
android/ inttypes.h memory.h stdint.h time.h
asm/ limits.h pathconf.h stdio.h unistd.h
asm-generic/ linux/ setjmp.h stdlib.h
dlfcn.h machine/ signal.h string.h
errno.h malloc.h stdarg.h strings.h

tcclib/include/android:
api-level.h

tcclib/include/asm:
errno.h page.h siginfo.h
fcntl.h posix_types.h signal.h
mman.h sigcontext.h types.h

tcclib/include/asm-generic:
errno-base.h fcntl.h mman.h signal.h
errno.h mman-common.h siginfo.h

tcclib/include/linux:
capability.h fcntl.h stddef.h
compiler.h limits.h time.h
errno.h posix_types.h types.h

tcclib/include/machine:
_types.h kernel.h setjmp.h
internal_types.h limits.h

tcclib/include/sys:
_types.h limits.h syslimits.h ucontext.h
_wchar_limits.h mman.h sysmacros.h
cdefs.h select.h time.h
cdefs_elf.h sysconf.h types.h

整理してシンプルなものにしたいんだが、regex とか追加したいような気もする。追加する場合は、整合性上さわらない方が良いだろう。今のままだと gcc から引っ張ってきているだけなので、gcc との整合性も取れている。
さて、例のブログに tcc でコンパイルする場合の変更点が載っているので、同様に修正。
やってみたところ、sys/cdefs.h に修正が必要だった。で、

tcc: error: undefined symbol '__aeabi_lasr'
tcc: error: undefined symbol '__aeabi_d2ulz'
tcc: error: undefined symbol '__aeabi_d2lz'

出た。よくは分からないが libgcc.a のようなものが必要なのだろう。

long long __aeabi_llsr(long long, int)
-- logical shift right [1]
long long __aeabi_lasr(long long, int)
-- arithmetic shift right [1]
int __aeabi_lcmp(long long, long long)
-- signed long long comparison [3]
int __aeabi_ulcmp(unsigned long long, unsigned long long)
-- unsigned long long comparison [3]

int __aeabi_d2iz(double)
-- double to integer C-style conversion [3]
unsigned __aeabi_d2uiz(double)
-- double to unsigned C-style conversion [3]
long long __aeabi_d2lz(double)
-- double to long long C-style conversion [3]
unsigned long long __aeabi_d2ulz(double)
-- double to unsigned long long C-style conversion [3]
int __aeabi_f2iz(float)
-- float (single precision) to integer C-style conversion [3]

ぐぐったら、Run-time ABI for the ARM(R) Architecture と冠する pdf があってこんなのがいっぱいある。... 困ったどうしたものか?
あれ? コンパイルだけやって undefined をチェックすると たくさんあるうちの3つがエラーになっている。他の実体はどこに?

U __aeabi_d2lz
U __aeabi_d2ulz
U __aeabi_idiv
U __aeabi_idivmod
U __aeabi_l2d
U __aeabi_lasr
U __aeabi_ldivmod
U __aeabi_llsl
U __aeabi_llsr
U __aeabi_memcpy4
U __aeabi_memcpy8
U __aeabi_memset
U __aeabi_uidivmod
U __aeabi_ul2d
U __aeabi_uldivmod

libgcc.a みたいなものと書いたが、libgcc.a に定義されていた。... というか libc とかも gcc でコンパイルされているし .. 。でも、libgcc.a そのものを指定すると別の undefined エラーが。解決するには、collect2 が必要になってしまう。とりあえず、自己完結しているものを集めて libtcc.a を作ったところ ... tcc が、ビルドできた!

あとは、ライブラリサーチパスとか crt ファイルの変更だとかをやって整理。

config.h:
#define CONFIG_TCCDIR "/system/xbin"
#define CONFIG_TCC_CRTPREFIX "/system/tcclib/lib:/data/local/tcclib/lib:/sdcard/tcclib/lib"
#define CONFIG_TCC_SYSINCLUDEPATHS \
"/system/tcclib/include:/data/local/tcclib/include:/sdcard/tcclib/include"
#define CONFIG_TCC_LIBPATHS "/system/lib:/system/tcclib/lib:/data/local/tcclib/lib:/sdcard/tcclib/lib"
#define CONFIG_TCC_ELFINTERP "/system/bin/linker"

#define HOST_ARM 1
#define TCC_ARM_VERSION 7
#define TCC_VERSION "0.9.26"

まぁ場当たり的な修正ばかりだが、一応は動く。

まとめ


 ・tcc-0.9.26-android-150316.tar.gz ソース+バイナリ版
 ・tcc-android-150316.tar.gz バイナリ版

最初の成果物。ソース+バイナリ版は、build-tcc.sh で tcc 自身がビルドできる。
バイナリ版は、/system/xbin に tcc を /system/tcclib に include と lib を置くように作成。
もうすこし詳しく書いておく。
tcclib を置けるのは、/system /data/local /sdcard のどれかの配下。tcc コマンドはパスが通っていれば良い。
ライブラリの指定は、-lc -ldl あたりだと思うが、なしでいける場合もある。long long を使うようなコードでは、-ltcc を付けないといけない場合がある。
-run オプションも動くようだ。

おまけ GCC のビルドオプション



COLLECT_GCC=/usr/local/bin/gcc
Target: arm-linux-androideabi
Configured with: ../configure --host=arm-linux-gnueabi \
--target=arm-linux-androideabi \
--enable-static --disable-shared --enable-languages=c,c++ \
--enable-initfini-array --disable-lto --with-float=soft --with-fpu=vfp \
--with-arch=armv5te --disable-libquadmath \
--disable-multilib --disable-libgomp \
--disable-libmudflap --enable-target-optspace --enable-threads \
--disable-libatomic --disable-tls --disable-nls \
--disable-sjlj-exceptions \
--disable-libstdc++-v3 --disable-libsanitizer --disable-plugins \
--disable-libgcc --disable-libssp --disable-docs --disable-libitm \
--with-gnu-as --with-gnu-ld --prefix=/prog/c4droid/OutDir \
--with-mpc=/prog/c4droid/mpc --with-mpfr=/prog/c4droid/mpfr \
--with-gmp=/prog/c4droid/gmp
Thread model: posix

おわりに


セルフで動く tcc をビルドするという目的は、達成されてしまった。アドホックなんで不具合は出るかも知れない。優先度は高くないが、手直しはしていきたい。
さて、次は gcc をセルフで動かすように整理したい。/system ではなく loop デバイスで /usr パーティションを mount して使うのが良いかと思っている。これもボチボチやりたいが、バイナリは配布しないかも。
次は、Mediatek のカーネルについて調べてみたい。
posted by すz at 01:49| Comment(0) | TrackBack(0) | android
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
この記事へのトラックバックURL
http://blog.sakura.ne.jp/tb/115155177
※ブログオーナーが承認したトラックバックのみ表示されます。

この記事へのトラックバック