Open11

WasmLinux: Musl libc移植方法の調査

okuokuokuoku

流石にダイナミックリンカは自作する必要があるので、その前にMusl libc https://musl.libc.org/ を移植しようとしたらどうなるのかを事前に調査しておく。

WASIはcloudlibcとmuslを混ぜてlibcを作っているが、今回のWasmLinuxでは基本的にLinux側のsyscallは殆ど揃っている(clone3はエミュレーションするつもり)ので単純にmusl全体をforkしてパッチする方向にしたい。

okuokuokuoku

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__ 等のシンボルは既に提供されている。

okuokuokuoku

欠けているヘッダファイル類

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.ininclude/asm-generic/unistd.h から該当行を抜けば良いね。

ここまでで ./configure は通った。

okuokuokuoku

欠けているヘッダファイル類 (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 はとりあえず空で。

okuokuokuoku

欠けているヘッダファイル類 (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);
}
okuokuokuoku

欠けているヘッダファイル類 (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.
okuokuokuoku

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
okuokuokuoku

__original_main どこから来たんだよ問題

wasm-ld: error: /home/oku/repos/musl/prefix/lib/crt1.o: undefined symbol: __original_main

... 消えたんじゃなかったの。。?

https://github.com/bytecodealliance/wizer/issues/58

とりあえず main の代わりに __original_main を使っておくか。。

okuokuokuoku

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する。

okuokuokuoku

結構グローバルが出る

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 みたいなシンボルをマクロにしてしまって間接参照させるか。。