TinyCC on ANDROID (ARM)

#TinyCC on ANDROID (ARM)

最近流行っているようなのでAndroidタブレットを入手し、いろいろいじってみている。
ARMにも対応している[TinyCC](http://bellard.org/tcc/)というコンパクトなC言語処理系を見つけた。
順を追って「タブレット上で」動かしてみるまでのメモである。
 ※[2014/05/03] 番外編3 tccelf.c差分さしかえ
 ※[2014/05/08] "sys/ucontext.h" 欠落への対処例などを追記

----------

AndroidのベースシステムはLinuxであり、UNIX系OSでの一般的なコンソールツール類も限定的に入っている。Google Playにも数種類の端末エミュレータが公開されている。

コンソールでコマンドがたたけるのなら、Hello,Worldをコンパイルしたくなるのが人情であろう。

WWWで情報をあさってみたところ、いくつか選択肢があるようだ。
 1) (本家Googleの)Android NDK
 2) KBOX2システム上のgccパッケージ
 3) C4droid
 4) AIDE

1) Android NDK 
https://developer.android.com/tools/sdk/ndk/index.html
ここを見ている人にはいまさら説明は不要だろう。巨大すぎてインストールして **いない** 。

2) KBOX2システム
ここ。http://kevinboone.net/kbox2.html
パスをすりかえることで、仮想的に一般なLinuxのディレクトリ構造を再現するシステムのようだ。rootを取ってない端末でも使える、とのこと。パッケージとしてGCCが用意されている。
ちょっと興味を引かれたが、gccパッケージが数十MB、展開時100MBとか。ちょっと却下。

3) C4droid
￥300程度ではあるが、有料という時点で手を出せていない。
核になっているコンパイラ本体はTinyCCらしい。IDEは便利かもしれないが筆者の用法に合うか不明。

4) AIDE
端末上でAndroid開発ができるIDE込みのシステム。
AndroidSDKのEclipsプロジェクトと互換性があり、AndroidNDKのC/C++開発もできる、とのこと。
ただ端末内購入という表示も少々引っかかる。

Hello,World に毛が生えた程度の簡単なプログラムを、思いついたときにさっと動かす環境がほしい。
標準的なLinuxシステム一式が欲しいわけじゃないし、便利なツール類をソース移植したいわけじゃない。大規模なAndroidネイティブアプリやJNIモジュールを開発したいわけでもない。

私はDOS上がりだ。おそらくUNIX系OSの習熟者がささっとスクリプトを書いて済ますところでずっとバッチファイルやCコンパイラを使ってきた、というだけだ。鉛筆を削るには肥後守で十分、高精度のNC工作機械一式は大げさである。

----------

###筆者の環境について:
以下の各項の記述は筆者の環境が基準である。追体験を試みる場合は適宜自分の環境に読み替えてもらいたい。

####開発マシン:
 OS: Windows 7pro
 所有コンパイラセット:
   * Microsofy Visual-C++ 2008 express 
   * MINGW/MSYS
 Android NDK: なし

####ターゲット(タブレット):
 VITRO P70-1GD8D-B
 OS:  Android 4.1.1
 プロセッサ: Amlogic8726-MXs ( Cortex A9 Dual Core)
 ※ このタブレットには最初からbusyboxがインストールされていたし、ファーム一式も公開されている。なにか悪さをしても、もとに戻せるのは心強い。

----------

###現在の状況:
[1] <済> コンパイラソースの入手
[2] <済> VisualC++ 2008でWin-TargetなTinyCCをコンパイル
[3] <済> TinyCC自身でWin-TargetなTinyCCをコンパイル
[4] <済> TinyCC自身でARM-Targetなクロスコンパイラを作成 
[5] <済> ARM-TargetなクロスコンパイラでAndroid用Hello,World
[6] <暫定> ARM-TargetなクロスコンパイラでAndroid用TinyCCセルフコンパイラを作成
[7] <今後の課題> Android上でTinyCC自身をビルドできる環境を作る

----------

##[1] コンパイラソースの入手
[TinyCC 0.9.26](http://bellard.org/tcc/)
最終的にARMターゲットのコンパイラが欲しいのでソースを入手。
Windows用のTCCバイナリも配布されてるので、[2]～[3]は興味が無ければ飛ばしてよい。
もともとAndroidNDKを入れてるHDDリッチな人は[4]～[5]もすっ飛ばして直接NDKでTinyCCをコンパイルすればよい。

----------

##[2] VisualC++ 2008でWin-TargetなTinyCCをコンパイル
せっかくソースを手に入れたので実行ファイルを作ってみる。unix流儀の configure;make;make install はあまり好きじゃない。
親切にもソースセットには"win32"というディレクトリがあり、mingw32環境でmakeを使わずに構築するためのバッチファイルが添付されている。
mingw32-gccと同様にランタイムとしてMSVCRT.DLLを利用する形態のWindows用コンパイラを作成できる。

このバッチファイルのコンパイラの指定を少し書き換えるだけでVisual C++でもコンパイルできる。
バッチファイルの動きを見ると、

1) 最初にユーティリティを2本コンパイルする
   tiny_impdef.exe
   tiny_libmaker.exe
2) libtcc.dll を作る。ついでにlibtcc.dllの.defファイルもつくる。
3) libtcc.dllとリンクする形でtcc.exeを作る。
4) 3)で作ったコンパイラでスタートアップルーチンをコンパイルし、ライブラリ化する。

という流れ。

コンパイラ指定部分

```bat:
@set CC=gcc -Os -s -fno-strict-aliasing
```

を

```bat:
@set CC=cl /Ox
```

に、

リンカの指定部分は

```bat:
%CC% %target% -shared -DLIBTCC_AS_DLL -DONE_SOURCE ../libtcc.c -o libtcc.dll -Wl,-out-implib,libtcc/libtcc.a
```

を

```bat:
%CC% %target% -DLIBTCC_AS_DLL -DONE_SOURCE ..\libtcc.c  /LD /link /IMPLIB:libtcc\libtcc.lib
```

に、

```bat:
%CC% %target% ../tcc.c -o tcc.exe -ltcc -Llibtcc
```

を

```bat:
%CC% %target% ..\tcc.c libtcc\libtcc.lib
```

にするとよい。

※いまどきWin9x+Visual C/C++6.0で頑張っている人は少ないと思うが、絶滅はしてないだろう。筆者もつい最近までそうだった。
その場合、long long を使用している箇所を__int64に置き換えたり不足関数を実装するなどソース側にも手を入れる必要があるが、むりやりコンパイルすることはできる。

※ VC版はLIBCMT.LIBとスタティックリンクするとややサイズが大きい。コンパイル時/MDを追加するとMSVCRT.DLLとリンクし身軽にすることもできる。
この場合、.manifestファイルはexeなり.dllに埋め込んでしまえばよいが、バッチファイルにさらにひと手間かかる。

----------

##[3] TinyCC自身でWin-TargetなTinyCCをコンパイル
出来上がったtcc.exeのチェックを兼ねて、tcc自身でTinyCCをコンパイルしてみる。
VC版と同様に元のmingw32用バッチのコンパイラ指定をtccに書換えればよい。コードは省略する。
少々ややこしいが
  vc版tcc.exe - (1)
  (1)で作った self-tcc.exe -(2)
  (2)でさらに作った chk-tcc.exe  -(3)
(2)と(3)がバイナリ一致することが確認できたのでよしとしよう。

----------

##[4] TinyCC自身でARM-Targetなクロスコンパイラを作成 
オリジナルのMakefileを見るとarm-eabi用コードを出力するクロスコンパイラを作るためには 3つのdefineを指定するだけでよさそうである。

```bat:
 -DTCC_TARGET_ARM -DTCC_ARM_EABI -DWITHOUT_LIBTCC
```

また、configureのほうでは、 `ARCH=arm` の場合config.hに次の行を出力してるので手で追加。

```c:
#define HOST_ARM 1
#define TCC_ARM_VERSION 7
```

なお、ありがたいことに、`-DONE_SOURCE` としてやると次々.cファイルをインクルードして1本ソース扱いでコンパイルできるようだ。
分割コンパイルして後でリンクする手間が省けるので指定する。

tcc.exe([2]～[3]フェーズで作ったもの)にパスを通しておくと、こんなバッチファイルとヘッダだけで
arm用のクロスコンパイラが手に入る。

```bat:build-cross.bat
@echo off
setlocal
set CPPFLAGS=-I.
set CFLAGS=-DONE_SOURCE -DTCC_TARGET_ARM -DTCC_ARM_EABI -DWITHOUT_LIBTCC -D_WIN32
set CC=tcc
%CC% %CPPFLAGS% %CFLAGS% ../tcc.c -o arm-eabi-tcc.exe
endlocal
```

```c: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"
```

思ったより簡単だが、[2]～[3]でいろいろ試してみてたどり着いた結果であり、その意味でセルフコンパイルをやってみて良かった。

プログラム実行時にlibc.soと動的にリンクするために実行ファイルにはダイナミックリンカの指定を埋め込む。
そのため、`CONFIG_TCC_ELFINTERP` に `"/system/bin/linker"` と指定をしておく。
ついでに、デフォルトのインクルード/ライブラリパスもandroid環境に合わせ設定しておく。

※ [余談] 当初MSYS上で `configure --xxx... ;make` を試してみていたのだが、
Makefile上のコマンドラインでは `"/system/bin/linker"` となっていても、生成ファイルには
`"c:/MinGW/msys/1.0/system/bin/linker"` と変換されて埋め込まれてしまう。これはmsysのsh.exeが勝手にコマンドライン指定を書換えてMSYSのパス扱いしてしまうせい。開いた口がふさがらなかった。

----------

##[5] ARM-TargetなクロスコンパイラでAndroid用実行ファイル(HelloWorld)を作成してみる。

前節で(少なくとも-cオプションで)armコードのELFオブジェクトが手に入るようになった。
が、端末上の実行ファイルとしては、
1) スタートアップルルーチン
2) 標準Cライブラリ
3) 2)のライブラリ向けのインクルードファイル一式
を入手する必要がある。
幸い、TinyCCはリンカ機能を内蔵しており、.soファイルからも直にリンクできる、とある。
よってlibcは端末の/system/lib/libc.soを直接使うことにする。
printf程度なら、とりあえずTinyCC付属のtcclib.hが使える。

スタートアップは暫定的に本家Android NDKから拝借する。

[ここ](https://developer.android.com/tools/sdk/ndk/index.html)からNDKは入手できるが、zipの状態で500MB近くある。

全部が欲しいわけではないので躊躇していたが、[androidのgitリポジトリ](https://android.googlesource.com/)
をあさってみると、フォルダ単位で部分ダウンロードできることに気づいた。

Android 4.1.1用なら下記あたりからもってくればよい。
https://android.googlesource.com/platform/prebuilts/ndk/+/android-4.1.1_r6.1/android-ndk-r7/platforms/android-14/arch-arm/usr
https://android.googlesource.com/platform/prebuilts/ndk/+/master/9/platforms/android-14/arch-arm/usr

`[tgz]`と書いてあるリンクからincludeとlib一式をダウンロード。
これに含まれる `lib/libc.so` はリンク用のガワだけのようで実行コードは含まれてない。
シンボルも少し足りないようなのでタブレットから本物の `/system/lib/libc.so` を吸い出して使ったほうが良い。
スタティックライブラリ(libc.a)も手に入るが、せっかくだからlibc.soをダイナミックリンクする形にこだわりたい。

スタートアップは `crt1.o` + `crti.o` + `crtn.o` の構成ではなく、`lib/crtbegin_dynamic.o` と `lib/crtend_android.o` の2本でよい。
`libc.a`をスタティックリンクするなら、`lib/crtbegin_static.o` と `lib/crtend_android.`o の2本でよい。

```c:hello.c
#include <tcclib.h>
int main() {printf("Hello,Android\n");return 0;}
```

`
arm-eabi-tcc.exe -nostdlib -I. crtbegin_dynamic.o hello.c crtend_android.o -ldl -lc -o hello
`

できた"hello"をタブレットの/sdcardにコピーする。
/sdcardからは直接実行できないので、Android Terminal Eulatorのホームディレクトリにコピーして実行。

```console:
$ cd ~
$ cp /sdcard/hello .
$ ./hello
Hello, Android
$
```

とりあえず動く。すなわちAndroid NDKを全部入れずとも、最低限のクロス開発環境が準備できた。
※ なお、この状態ではstdout/stderrを指定したfprintf()が使えないことが後でわかった。対処については後述の番外編3を参照されたし。

----------

##[6] ARM-TargetなクロスコンパイラでAndroid用TinyCCセルフコンパイラを作成。
いよいよ当初の目的である「Androidのコンソール上で動くtcc」をビルドする。
さすがにtcclib.hではぜんぜん足りないので、ヘッダ一式はNDKのものを借用する。

足りないヘッダはやはりNDKのクロスgccのヘッダで補う。
GCCキメ打ち部分や__buildin_xxxx等、多少手を入れる必要がある。

https://android.googlesource.com/platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.6/+/master/lib/gcc/arm-eabi/4.6.x-google/include/

```diff:
--- gcc/include_org/stddef.h	Mon Apr 28 18:15:39 2014
+++ gcc/include/stddef.h	Mon Apr 28 11:40:44 2014
@@ -410,7 +410,11 @@
 #ifdef _STDDEF_H
 
 /* Offset of member MEMBER in a struct of type TYPE. */
+#ifdef  __TINYC__
+#define offsetof(TYPE, MEMBER)  ((size_t)&((TYPE *)0)->MEMBER)
+#else
 #define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER)
+#endif
 
 #endif /* _STDDEF_H was defined this time */
``` 

```diff:
--- gcc/include_org/stdarg.h	Mon Apr 28 18:15:39 2014
+++ gcc/include/stdarg.h	Mon Apr 28 11:36:43 2014
@@ -37,13 +37,23 @@
 
 #ifndef __GNUC_VA_LIST
 #define __GNUC_VA_LIST
+#ifdef __TINYC__
+typedef char * __gnuc_va_list;
+#else
 typedef __builtin_va_list __gnuc_va_list;
 #endif
+#endif
 
 /* Define the standard macros for the user,
    if this invocation was from the user program.  */
 #ifdef _STDARG_H
 
+#ifdef __TINYC__
+#define va_start(ap,last) ap = ((char *)&(last)) + ((sizeof(last)+3)&~3)
+#define va_arg(ap,type) (ap += (sizeof(type)+3)&~3, *(type *)(ap - ((sizeof(type)+3)&~3)))
+#define va_copy(dest, src) (dest) = (src)
+#define va_end(ap)
+#else
 #define va_start(v,l)	__builtin_va_start(v,l)
 #define va_end(v)	__builtin_va_end(v)
 #define va_arg(v,l)	__builtin_va_arg(v,l)
@@ -51,6 +61,7 @@
 #define va_copy(d,s)	__builtin_va_copy(d,s)
 #endif
 #define __va_copy(d,s)	__builtin_va_copy(d,s)
+#endif
 
 /* Define va_list, if desired, from __gnuc_va_list. */
 /* We deliberately do not define va_list when called from
```

```diff
--- include_org/asm/posix_types.h	Tue Aug 21 14:23:12 2012
+++ include/asm/posix_types.h	Mon Apr 28 16:58:29 2014
@@ -41,3 +41,3 @@
 
-#ifdef __GNUC__
+#if defined(__GNUC__) || defined(__TINYC__)
 typedef long long __kernel_loff_t;
```

まだ`sys/ucontext.h`が足りないが、
  a) bionic(androidのlibc)の"master"版に後で追加されたのを持ってくる。(`sys/user.h`も要る。)
  b) "ucontext.h android"で検索するとそれらしい構造体定義が公開されているのでコピって使う。
  c) "-run"オプション動作で必要なので、コンパイルだけが目的なら"-run"関連動作を殺せば要らなくなる。(`tccrun.c`冒頭の`#ifdef TCC_IS_NATIVE` を `#if 0` に変更すればよい)
等の方法で回避する。

足りない関数類は作って追加するしかない。
`strtold()`は精度を無視して`strtod()`で代用。自分では64bit浮動小数なんか使うプログラムを書く予定は無い。

```c:__aeabi_lasr.c
#include <stdint.h>
uint64_t __aeabi_lasr( uint64_t v, int cnt) 
{
	int c = cnt & 31;
	uint32_t vl = (uint32_t) v;
	uint32_t vh = (uint32_t) (v >> 32);

	if (cnt & 32) {
		vl = ((int32_t) vh >> c);
		vh = (int32_t) vh >> 31;
	} else {
		vl = (vl >> c) + (vh << (32 - c));
		vh = ((int32_t) vh >> c);
	}
	return ((uint64_t) vh << 32) + vl;
}
long double strtold(const char *s, char **p) { return (long double)strtod(s, p);}
```

ビルド用のバッチファイルはこんな感じ。

```bat:build.bat
@setlocal

@set CPPFLAGS=-I. -I./gcc/include -I./include
@set CFLAGS=-nostdlib -DONE_SOURCE -DTCC_TARGET_ARM -DTCC_ARM_EABI -D_STANDALONE -DWITHOUT_LIBTCC -D__arm__ -D__ARM_ARCH_7A__ -D__ARM_ARCH__=7 -D__STDC_HOSTED__=1 -D__WCHAR_MAX__=4294967295UL 
@set LFLAGS=-L./lib -lc -lm -ldl
@set CRT_BEGIN=lib/crtbegin_dynamic.o
@set CRT_END=lib/crtend_android.o
@set CC=arm-eabi-tcc

     %CC% %CPPFLAGS% %CFLAGS% %CRT_BEGIN% ../tcc.c __aeabi_lasr.c  %CRT_END% -o tcc %LFLAGS%

@endlocal
```

出来上がった `tcc` を前と同様に/sdcard経由で端末に送り込んで適当なソースをコンパイルしてみる。

```console:
$cd ~
$cp /sdcard/tcc/tcc .
$cp /sdcard/tcc/lib/crt*.o .
$cp /sdcard/tcc/hello.c .
$./tcc -I. -nostdlib crtbegin_dynamic.o hello.c crtend_android.o -ldl -lc -o hello
$./hello
Hello, Android
$
```

----------

##[7] <今後の課題> Android上でTinyCC自身をビルドできる環境を作る
できればNDKからの借用もやめたいし、GCCの影も消したい。
* スタートアップの自作
* C99(JIS X3010)準拠のシンプルなヘッダセット構築
* etc,...

----------

##[番外編1] -Iや-Lで別ドライブのパスを指定する。
TinyCCでは `TCC_TARGET_PE` 以外、すなわちwin32セルフコンパイラとして作成する場合を除き、パス区切り文字として":"を使用している。
クロスコンパイラで-Iや-Lで別ドライブの指定を含むフルパスを指定すると、うまく見つけてくれない。
とりあえず2文字目で"[a-zA-Z]:/"は区切りと解釈しないようにするパッチ。
_WIN32定義で有効になるようにしてある。

```diff:
--- libtcc.c_org	Fri Feb 15 23:24:00 2013
+++ libtcc.c	Wed Apr 23 19:56:02 2014
@@ -306,11 +306,20 @@
 {
     const char *p;
     do {
-        int c;
+        int c,l;
         CString str;
 
         cstr_new(&str);
-        for (p = in; c = *p, c != '\0' && c != PATHSEP; ++p) {
+        for (p = in,l=0; c = *p, c != '\0' /*&& c != PATHSEP*/; ++p,l++) {
+			if ( c == PATHSEP ) {
+#ifdef _WIN32
+				if ( c == ':' && l==1 && isalpha(p[-1]) && (p[1]=='\\'||p[1]=='/') ) {
+					cstr_ccat(&str, c); 
+					continue;
+				}
+#endif
+				break;
+			}
             if (c == '{' && p[1] && p[2] == '}') {
                 c = p[1], p += 2;
                 if (c == 'B')
```
----------

##[番外編2] ShiftJisな文字列
これはWin32上でセルフコンパイラとして使うときの問題。
ShiftJis 2バイト目の0x5cをエスケープ文字'￥'と解釈してしまうのを抑制する。
文頭からの完全な文字列解釈ではないが、よほど変な文字列を食わせない限り実用上はこの程度で十分だろう。
`TCC_CHAR_SJIS` 指定で有効になるようにしてある。

```diff:
--- tccpp.c_org	Fri Feb 15 23:24:00 2013
+++ tccpp.c	Fri Apr 25 00:41:57 2014
@@ -1641,7 +1641,7 @@
 /* evaluate escape codes in a string. */
 static void parse_escape_string(CString *outstr, const uint8_t *buf, int is_long)
 {
-    int c, n;
+    int c, n, sj=0;
     const uint8_t *p;
 
     p = buf;
@@ -1649,7 +1649,7 @@
         c = *p;
         if (c == '\0')
             break;
-        if (c == '\\') {
+        if (c == '\\' && !sj) {
             p++;
             /* escape */
             c = *p;
@@ -1737,6 +1737,9 @@
             cstr_ccat(outstr, c);
         else
             cstr_wccat(outstr, c);
+#ifdef TCC_CHAR_SJIS
+        sj=(!sj&&((c>=0x81&&c<=0x9f)||(c>=0xe0&&c<=0xfc)))?1:0;
+#endif
     }
     /* add a trailing '\0' */
     if (!is_long)
```
----------

##[番外編3] stdout/stderrの回避策 [暫定対応]
WWWでは「Androidではstdout/stderrをサポートしてない」と断言しているページも有るようだが、おそらく真相は/system/bin/linkerのバグのせいではないか。

[こちら](http://dan.drown.org/android/worknotes.html)のページがヒントになった。
`/system/bin/linker` が、 **R_ARM_COPY** タイプのデータをきちんとリンクしてくれてないようだ。

もともとの `stdio.h` のstdout/stderrの定義が
 
```c:
extern FILE __sF[];
#define stdin  (&__sF[0])
#define stdout (&__sF[1])
#define stderr (&__sF[2])

```

となっている。

tccのコード生成部分は配列の実体をアクセスするようなコードを生成しているらしい。
stdoutやstderrはオフセット加算済みの形でリンク時に解決してもらうつもりになっている。
また、リンクのフェーズではlibc.so由来の__sFというシンボルを見つけたときには、.bssセクション中に **R_ARM_COPY** タイプの同名のエントリを生成して、コード部分は.bss上の実体を参照するようにしてるらしい。
すなわち、プログラム実行の際、おそらく通常のダイナミックリンカの場合は
  a) ライブラリの__sF[]データを初期化した後に、実行ファイル側の.bssに中身をコピー
  b) ライブラリ内部コードが__sF[]を参照するときに実行ファイル側の.bss内のアドレスを参照するように細工
みたいな動きをすることを期待してるんじゃないかと思う。

Android 4.1.1の/system/bin/linker は a)の部分がバグっていてコピーを行っていない。(4.2で修正が入っているらしい)
また、未確認だがb)の部分が存在せず、ライブラリはライブラリ側の、実行ファイルは実行ファイル側の__sF[]をそれぞれ参照しているふしがある。
(そう考えると、printfみたいな内部的にstdoutを使用してるであろう処理が動いてるのも説明がつく)

一方上記のページの「うまく動くケース」の場合、gccでは.bss上の **R_ARM_COPY** データだけではなく、.gotにエントリを生成して、間接ポインタ経由で動くようなコードも生成してるようだ。
つまり/system/bin/linkerでも、.got上のエントリに対して &(__sF) の形でアドレスを代入してくれるところは機能しているようだ。

そこで、暫定的な回避策として
1) ヘッダに細工して、stdoutやstderrを参照する箇所ではポインタを経由する形のコードになるようにする。
2) libc.soとリンクする際.bssにコピーデータ( **R_ARM_COPY** )を作るところをやめて.gotエントリだけ作る。
3) .dynasymでは無条件に.pltのオフセット作っていたところを、関数なら.plt、DATAなら.got上のアドレスを計算させるように修正。

linkerには.got上のグローバルオフセットとしてアドレスを設定させ、プログラム側にはその.got上のアドレス値をポインタ変数であるかのように認識させることでうまくいきそうだ。
やってみたら狙い通り、stdout/stderrも使えるようになった。

このパッチによる制約事項:
外部ライブラリとダイナミックリンクする場合、ライブラリ側が持っている大域変数はポインタとして参照する必要があるということ。
すなわち、 `__sF[]` のように配列として宣言されてるものなら

```c:
  extern FILE * __sF;
```
とすることで擬似的に同じような形で扱えるが、それ以外の形式では形が変わってしまう。

```diff:
--- include_org/stdio.h	Fri Dec 21 23:31:29 2012
+++ include/stdio.h	Mon Apr 28 17:20:37 2014
@@ -132,3 +132,7 @@
 __BEGIN_DECLS
+#if defined(TCC_ANDROID_WORKAROUND)
+extern FILE *__sF;
+#else
 extern FILE __sF[];
+#endif
 __END_DECLS
```

```diff:
--- tccelf.c_org	Fri Feb 15 23:24:00 2013
+++ tccelf.c	Fri May 02 21:45:37 2014
@@ -1643,6 +1643,15 @@
                                               ELFW(ST_INFO)(STB_GLOBAL,STT_FUNC),
                                               sym - (ElfW(Sym) *)symtab_section->data);
                             } else if (type == STT_OBJECT) {
+#if defined(TCC_TARGET_ARM) && defined(TCC_ANDROID_WORKAROUND)
+                              int * ptr;
+                              index = put_elf_sym(s1->dynsym, s1->got->data_offset, PTR_SIZE, 
+                                          esym->st_info, 0, sym->st_shndx, name);
+                              put_elf_reloc(s1->dynsym, 
+                                          s1->got, s1->got->data_offset, R_ARM_GLOB_DAT, index);
+                              ptr = section_ptr_add(s1->got, PTR_SIZE);
+                              *ptr = 0;
+#else
                                 unsigned long offset;
                                 ElfW(Sym) *dynsym, *dynsym_end;
                                 offset = bss_section->data_offset;
@@ -1676,6 +1685,7 @@
                                               offset, R_COPY, index);
                                 offset += esym->st_size;
                                 bss_section->data_offset = offset;
+#endif
                             }
                         } else {
                                 /* STB_WEAK undefined symbols are accepted */
@@ -2102,6 +2112,12 @@
                 if (sym->st_shndx == SHN_UNDEF) {
                     /* relocate to the PLT if the symbol corresponds
                        to a PLT entry */
+#if defined(TCC_TARGET_ARM) && defined(TCC_ANDROID_WORKAROUND)
+                    type = ELFW(ST_TYPE)(sym->st_info);
+                    if (sym->st_value && type==STT_OBJECT )
+                        sym->st_value += s1->got->sh_addr;
+                    else
+#endif
                     if (sym->st_value)
                         sym->st_value += s1->plt->sh_addr;
                 } else if (sym->st_shndx < SHN_LORESERVE) {

```