Mosh JIT で R6RS test suite が SEGV する原因はアラインメントだった
タイトルの通り落ちると思ったらアラインメントが原因だった。はまらずにすぐに原因を発見できた自分をほめたい。
0x000000000045c0ec in scheme::Flonum::iDiv (theVM=0xa20c80, argc=2, argv=<value optimized out>) at src/Flonum.h:375 375 return f2 > 0.0 ? ::floor(f1 / f2) : - ::floor(f1 / (-f2)); 0x000000000045c0e8 <_ZN6scheme14flIntegerDivExEPNS_2VMEiPKNS_6ObjectE+472>: xorpd %xmm1,%xmm2 0x000000000045c0ec <_ZN6scheme14flIntegerDivExEPNS_2VMEiPKNS_6ObjectE+476>: movapd %xmm1,0x10(%rsp) <== ここ 0x000000000045c0f2 <_ZN6scheme14flIntegerDivExEPNS_2VMEiPKNS_6ObjectE+482>: divsd %xmm2,%xmm0
最初はスタックオーバーフローかと思ったのだがよく調べると違った。マニュアルによると movapd は 16 byte アラインメントを要求するらしい。
(gdb) print $rsp + 0x10 $2 = (void *) 0x7fffffffbf28
これはだめだ。落ちているところは gcc がコンパイルした関数なので勝手にアラインメントしてくれれば良いのにと思って調べる。
いつもお世話になっている herumi さんのx64 Assembly Language Programmingで見てみると
gccでのスタック スタックは常に16byteアラインメントされています. ただし関数呼び出し直後は戻りアドレス(8byte)がpushされているため, 8(mod 16)となっています.
とある。つまり
gcc 関数 => jit コード => gcc 関数のように call していって、jit コードで 16 byte アラインメントが守られなくなってしまったのだろう。
jit で rsp をいじるのは専ら push/pop だけなので奇数個の push を偶数にすれば良かろうと思っているが今日はここまで。
自動で整列させるコードがありそうなので今度探そう。