WasmLinux: Musl libc移植方法の調査
流石にダイナミックリンカは自作する必要があるので、その前にMusl libc https://musl.libc.org/ を移植しようとしたらどうなるのかを事前に調査しておく。
WASIはcloudlibcとmuslを混ぜてlibcを作っているが、今回のWasmLinuxでは基本的にLinux側のsyscallは殆ど揃っている(clone3はエミュレーションするつもり)ので単純にmusl全体をforkしてパッチする方向にしたい。
long doubleチェックに通らない
とりあえず適当にconfigure。
$ AR=/home/oku/repos/linux/_warp/bin/warp-ar CC=/home/oku/repos/linux/_warp/bin/warp-cc \
CFLAGS=-fPIC ./configure --enable-debug --disable-shared --build=wasm32
checking whether compiler's long double definition matches float.h... no
./configure: error: unsupported long double type
テストしているのはこのコードで
#include <float.h>
#define C(m,s) (m==LDBL_MANT_DIG && s==sizeof(long double))
typedef char ldcheck[(C(53,8)||C(64,12)||C(64,16)||C(113,16))*2-1];
LDBL_MANT_DIG
= 113。これ自体はコンパイル通らないとおかしいので、コンパイラの実行エラーか。
In file included from ./conf96250-95719-1.c:1:
./include/float.h:46:10: fatal error: 'bits/float.h' file not found
46 | #include <bits/float.h>
| ^~~~~~~~~~~~~~
1 warning and 1 error generated.
まぁ適当に用意すれば良いな。
//#define LDBL_TRUE_MIN 3.6451995318824746025e-4951L
#define LDBL_MIN __LDBL_MIN__
#define LDBL_MAX __LDBL_MAX__
#define LDBL_EPSILON __LDBL_EPSILON__
#define LDBL_MANT_DIG __LDBL_MANT_DIG__
#define LDBL_MIN_EXP __LDBL_MIN_EXP__
#define LDBL_MAX_EXP __LDBL_MAX_EXP__
#define LDBL_DIG __LDBL_DIG__
#define LDBL_MIN_10_EXP __LDBL_MIN_10_EXP__
#define LDBL_MAX_10_EXP __LDBL_MAX_10_EXP__
predefined macroとして __LDBL_MANT_DIG__
等のシンボルは既に提供されている。
欠けているヘッダファイル類
make: *** 'obj/include/bits/alltypes.h' に必要なターゲット 'arch/wasm32/bits/alltypes.h.in' を make するルールがありません.
make: *** 'obj/include/bits/syscall.h' に必要なターゲット 'arch/wasm32/bits/syscall.h.in' を make するルールがありません.
make: *** 'obj/crt/crt1.o' に必要なターゲット 'arch/wasm32/crt_arch.h' を make するルールがありません.
make: *** 'obj/crt/rcrt1.o' に必要なターゲット 'arch/wasm32/reloc.h' を make するルールがありません.
crtは自前のものを実装する必要があるのでMuslのビルドターゲットからは除いておく必要があるな。。 alltypes.h.in
はarm32のを適当にコピペしておく。 syscall.h.in
は include/asm-generic/unistd.h
から該当行を抜けば良いね。
ここまでで ./configure
は通った。
欠けているヘッダファイル類 (1周目ビルド時)
-k
オプションでエラー時に停止しないようにして、エラーをファイルにリダイレクトして調査。
$ make -k 2>&1 | grep "fatal error" > out.txt
./include/setjmp.h:10:10: fatal error: 'bits/setjmp.h' file not found
./include/stdint.h:95:10: fatal error: 'bits/stdint.h' file not found
./src/include/../../include/signal.h:48:10: fatal error: 'bits/signal.h' file not found
./src/include/../../include/unistd.h:260:10: fatal error: 'bits/posix.h' file not found
./src/include/sys/../../../include/sys/stat.h:23:10: fatal error: 'bits/stat.h' file not found
./src/internal/syscall.h:7:10: fatal error: 'syscall_arch.h' file not found
src/internal/syscall.h:7:10: fatal error: 'syscall_arch.h' file not found
setjmp.h、stdint.h、signal.h、posix.h、stat.h は arm
からコピーした。setjmpやsignalはどうしたもんかな。。
syscall_arch.h
はとりあえず空で。
欠けているヘッダファイル類 (2周目ビルド時)
./src/internal/atomic.h:6:10: fatal error: 'atomic_arch.h' file not found
src/stat/fstatat.c:73:10: fatal error: 'kstat.h' file not found
kstat.h
はやっぱり arm
からコピー。
atomic_arch.h
はどうしたもんかな。。最悪 a_cas
だけで良さそう。
#define a_cas a_cas
static inline int
a_cas(volatile int *p, int t, int s){
return __sync_val_compare_and_swap(p, t, s);
}
欠けているヘッダファイル類 (3周目ビルド時)
./src/internal/pthread_impl.h:14:10: fatal error: 'pthread_arch.h' file not found
これはABIを検討してからじゃないとどうしようも無いやつだな。。
static inline uintptr_t
__get_tp(){
return 0;
}
#define MC_PC 0
これはエラーになる。そもそもWasm標準ではスレッドのcancelはやりようが無いので、APIごと潰すしか無いんじゃなかろうか。。
src/thread/pthread_cancel.c:52:33: error: expected identifier
52 | uintptr_t pc = uc->uc_mcontext.MC_PC;
| ^
./arch/wasm32/pthread_arch.h:6:15: note: expanded from macro 'MC_PC'
6 | #define MC_PC 0
| ^
src/thread/pthread_cancel.c:65:19: error: expected identifier
65 | uc->uc_mcontext.MC_PC = (uintptr_t)__cp_cancel;
| ^
./arch/wasm32/pthread_arch.h:6:15: note: expanded from macro 'MC_PC'
6 | #define MC_PC 0
| ^
2 errors generated.
プログラムカウンタ直接アクセスはどうしようもない
src/thread/__unmapself.c:23:2: error: call to undeclared function 'CRTJMP'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
23 | CRTJMP(do_unmap, stack);
| ^
この辺を消し込むことでとりあえず libc.a
は生成された。
ranlib必須なのかよ!
wasm-ld: error: /home/oku/repos/musl/prefix/lib/libc.a: archive has no index; run ranlib to add one
clang: error: linker command failed with exit code 1 (use -v to see invocation)
llvm-ranlib
を掛けたら治ったけど、GNUのranlibは何もしないんなら普通に失敗して欲しいと思う。。歴史的事情かもしれないけど。
AR=/home/oku/repos/linux/_warp/bin/warp-ar CC=/home/oku/repos/linux/_warp/bin/warp-cc \
CFLAGS=-fPIC RANLIB=llvm-ranlib ./configure --enable-debug --disable-shared --build=wasm32 \
--prefix=`pwd`/prefix
__original_main
どこから来たんだよ問題
wasm-ld: error: /home/oku/repos/musl/prefix/lib/crt1.o: undefined symbol: __original_main
... 消えたんじゃなかったの。。?
とりあえず main
の代わりに __original_main
を使っておくか。。
compiler-rt
そういや存在を忘れてたな。。
wasm-ld: error: /home/oku/repos/musl/prefix/lib/libc.a(frexpl.lo): undefined symbol: __eqtf2
wasm-ld: error: /home/oku/repos/musl/prefix/lib/libc.a(frexpl.lo): undefined symbol: __multf3
wasm-ld: error: /home/oku/repos/musl/prefix/lib/libc.a(vfprintf.lo): undefined symbol: __addtf3
wasm-ld: error: /home/oku/repos/musl/prefix/lib/libc.a(vfprintf.lo): undefined symbol: __extenddftf2
wasm-ld: error: /home/oku/repos/musl/prefix/lib/libc.a(vfprintf.lo): undefined symbol: __fixtfsi
wasm-ld: error: /home/oku/repos/musl/prefix/lib/libc.a(vfprintf.lo): undefined symbol: __fixunstfsi
wasm-ld: error: /home/oku/repos/musl/prefix/lib/libc.a(vfprintf.lo): undefined symbol: __floatsitf
wasm-ld: error: /home/oku/repos/musl/prefix/lib/libc.a(vfprintf.lo): undefined symbol: __floatunsitf
wasm-ld: error: /home/oku/repos/musl/prefix/lib/libc.a(vfprintf.lo): undefined symbol: __netf2
wasm-ld: error: /home/oku/repos/musl/prefix/lib/libc.a(vfprintf.lo): undefined symbol: __subtf3
wasm-ld: error: /home/oku/repos/musl/prefix/lib/libc.a(vfprintf.lo): undefined symbol: __unordtf2
まぁこれ以外は多分いらないから該当コードだけLLVMから拾ってくるのが良いかな。。まぁ今は要らないので適当にstubする。
結構グローバルが出る
typedef struct w2c_user {
struct w2c_env* w2c_env_instance;
u32 w2c_0x5F_stack_pointer;
u32 w2c_GOT0x2Efunc0x2Einternal0x2E_fini;
u32 w2c_GOT0x2Efunc0x2Einternal0x2E_init;
u32 w2c_GOT0x2Efunc0x2Einternal0x2E_0x5Foriginal_main;
u32 w2c_GOT0x2Edata0x2Einternal0x2E_0x5Fmemory_base;
u32 w2c_GOT0x2Edata0x2Einternal0x2E_DYNAMIC;
u32 w2c_GOT0x2Edata0x2Einternal0x2E_0x5Fenviron;
u32 w2c_GOT0x2Edata0x2Einternal0x2E_0x5Fprogname_full;
u32 w2c_GOT0x2Edata0x2Einternal0x2E_0x5Fprogname;
u32 w2c_GOT0x2Edata0x2Einternal0x2E_0x5Finit_array_end;
u32 w2c_GOT0x2Edata0x2Einternal0x2E_0x5Finit_array_start;
u32 w2c_GOT0x2Edata0x2Einternal0x2E_0x5Ftable_base;
u32 w2c_GOT0x2Edata0x2Einternal0x2E_0x5Ffini_array_start;
u32 w2c_GOT0x2Edata0x2Einternal0x2E_0x5Ffini_array_end;
wasm_rt_memory_t w2c_memory;
wasm_rt_funcref_table_t w2c_T0;
} w2c_user;
... 確かに _environ
とかはライブラリからimportしてこないと使えないな。。邪道だけど w2c_GOT0x2Efunc0x2Einternal0x2E_fini
みたいなシンボルをマクロにしてしまって間接参照させるか。。