Open5

WebGL-Native: Unityクラッシュする問題

okuokuokuoku

prev: https://zenn.dev/okuoku/scraps/06bf07df1a36a9
next: https://zenn.dev/okuoku/scraps/e085f787df3550

これはマジで解ける気がしない

というわけで諸々対応してUnityを起動してみたが、なぜか起動時に不正なC++仮想関数を実行してクラッシュしてしまう。

Invalid function pointer called with signature 'iiii'. Perhaps this is an invali
d value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling
a function with an incorrect type, which will fail? (it is worth building your s
ource files with -Werror (warnings are errors), as warnings can indicate undefin
ed behavior which can cause this)
Build with ASSERTIONS=2 for more info.
51
51
failed to asynchronously prepare wasm: Error: abort(51) at Error
    at jsStackTrace (eval at boot_unity (C:\cygwin64\home\oku\repos\cwgl\jstesta
pp\index.js:568:5), <anonymous>:739:12)
    at stackTrace (eval at boot_unity (C:\cygwin64\home\oku\repos\cwgl\jstestapp
\index.js:568:5), <anonymous>:753:11)
    at abort (eval at boot_unity (C:\cygwin64\home\oku\repos\cwgl\jstestapp\inde
x.js:568:5), <anonymous>:19:44)
    at nullFunc_iiii (eval at boot_unity (C:\cygwin64\home\oku\repos\cwgl\jstest
app\index.js:568:5), <anonymous>:14349:2)
    at cb (C:\cygwin64\home\oku\repos\cwgl\jstestapp\nccc-node.js:125:27)
    at Object.<anonymous> (C:\cygwin64\home\oku\repos\cwgl\jstestapp\node_module
s\ffi-napi\lib\callback.js:66:27)
    at Object.ffi_call (<anonymous>)
    at proxy (C:\cygwin64\home\oku\repos\cwgl\jstestapp\node_modules\ffi-napi\li
b\_foreign_function.js:61:14)
    at C:\cygwin64\home\oku\repos\cwgl\jstestapp\nccc-node.js:325:21
    at eval (eval at boot_unity (C:\cygwin64\home\oku\repos\cwgl\jstestapp\index
.js:568:5), <anonymous>:22318:79)
okuokuokuoku

落ちている関数を特定する

https://github.com/okuoku/cwgl-proto/commit/96a5dae9bf9c7af52caf642d5b5a74826b94aedd

とりあえず -g3 でデバッグ情報を付け、更に該当する関数にclangの組込み関数である __builtin_trap() を挟んで止めてみることにした。 VisualStudioのデバッガはDWARFも読めるようだ。 そもそも clang が COFF + PDB を出力しているようだ。。偉いね。

バックトレースのシンボルから、メモリマネージャっぽいことはわかる。バックトレースのnode → ネイティブ呼出しの始端である __GLOBAL__sub_I_PlatformDependent_WebGL_Source_2_cpp がWASM的なエクスポートで、これはEmscriptenの __ATINIT__ 配列に登録されているようだ。

okuokuokuoku

NULL ポインタの生成箇所を探す

ねっとりとステップ実行したところ、 NULL になっているのは↓の位置だった。

ブレークポイントを仕掛けているところから、矢印の場所まで実行する間に呼ばれたJavaScript側の関数は、

DO_call 0 0 [] __GLOBAL__sub_I_PlatformDependent_WebGL_Source_2_cpp
Called _getpagesize
In 321906464496 321906464480 _getpagesize
★↓↓ ここから
Call []
Called _getpagesize
In 321906463440 321906463424 _getpagesize
Call []
Called ___syscall192
In 321906462616 321906462600 ___syscall192
Call [ 192, 2346448 ]
DO_call 3 1 [ 16384, 1097728 ] _memalign
Called getTotalMemory
In 321906455232 321906455216 getTotalMemory
Call []
DO_call end
DO_call 4 1 [ 7592336, 0, 1097728 ] _memset
DO_call end
Called ___syscall91
In 321906462664 321906462648 ___syscall91
Call [ 91, 2346448 ]
Called ___syscall91
In 321906462664 321906462648 ___syscall91
Call [ 91, 2346448 ]
★★↑↑ ここまで

つまり、これらのAPIに console.log を仕込んで、Node.JSのWebAssemblyで実行した場合と結果を比較してみるのが良いかな。 syscall91 = munmapsyscall192 = mmap2

okuokuokuoku

memalignが正常に動作していない

まず wasm2c :

======= START INIT ======
Getpagesize 16384
Getpagesize 16384
GetTotalMemory 33554432
syscall192 memory 7592336 ★ 0x73D990 なので明かにページ境界に居ない
syscall192 success 7592336
Munmap 7592336 { malloc: 7592336, len: 1097728, allocated: true, fd: -1, flags: 34 }
Munmap 8650752 undefined

同じログをNode.JSのWebAssemblyで取ると:

======= START INIT ======
Getpagesize 16384
Getpagesize 16384
GetTotalMemory 33554432
syscall192 memory 7602176 ★ 0x740000 なのでアラインは取れている
syscall192 success 7602176
Munmap 8650752 undefined
Getpagesize 16384
======= END ======

つまり、 mmapページ境界に整列されていないアドレスが取れてしまっている 。これだと正常に動作しない。WebAssemblyでは mmap に相当する操作は実装できないのでこれはEmscriptenでエミュレートされていて、実際のアドレスの振り出しは memalign で行われるようになっている。

... たしかに memalign の第一引数(arg0 : アライン単位)にゼロが渡っている。これは未定義挙動になる。

okuokuokuoku

治った

https://github.com/okuoku/cwgl-proto/commit/c600f6e549c290bb939fe6dd76f5df2e7f839d3e#diff-092436732b8700327e332fd9621884389fede6f4fb22940992a9485ec5cf33a4L242-R242

... というわけで、findIndexで検索するにあたって false を詰めておいたら 0 でもヒットしてしまったという凡ミスだった。。初期値を -1 にしてゼロが当たるのを回避。

これでUnityのbootまで進んだけど超クッソ激烈に遅く全然起動しない。。流石に ffi-napi では限界を感じるのでちゃんと N-API なstubを用意した方が良いかな。。