Cygwinのgccのauto変数
CygwinのgccでビルドされたCプログラムでは関数に入るごとに勝手に _alloca
(aka __chkstk
) というアセンブリ・コードが挿入されるなんて知らなかったよ! PSI3のビルドでは libint/emit_order.c
の void emit_order()
および libderiv/emit_deriv{1,12}_managers.c
の void emit_deriv{1,12}_managers()
においてそのタイミングでSEGVっていた.Insightでデバッグを始めたときは関数に入って最初の変数の初期化より前に,ソースコードには影も形もない _alloca
そして probe
といったルーチンに入り,その中でSEGVるから,何事かと頭を抱えてしまったよ.普通ならauto変数で長すぎる配列を取るとstack overflowか何かのエラーになると思うが,gccはそこにalloca相当の補助ルーチンを挿入することでエラーにならないようにしてくれるらしい.しかし今回の問題は,それがCygwinではうまく機能してないってことかな? (単にスタックの長さを調べているだけという話もある.まだ理解してない)
/* Function prologue calls _alloca to probe the stack when allocating more than CHECK_STACK_LIMIT bytes in one go. Touching the stack at 4K increments is necessary to ensure that the guard pages used by the OS virtual memory manger are allocated in correct sequence. */ .global ___chkstk .global __alloca #ifndef _WIN64 ___chkstk: __alloca: pushl %ecx /* save temp */ leal 8(%esp), %ecx /* point past return addr */ cmpl $0x1000, %eax /* > 4k ?*/ jb Ldone Lprobe: subl $0x1000, %ecx /* yes, move pointer down 4k*/ orl $0x0, (%ecx) /* probe there */ subl $0x1000, %eax /* decrement count */ cmpl $0x1000, %eax ja Lprobe /* and do it again */ Ldone: subl %eax, %ecx orl $0x0, (%ecx) /* less than 4k, just peek here */ movl %esp, %eax /* save old stack pointer */ movl %ecx, %esp /* decrement stack */ movl (%eax), %ecx /* recover saved temp */ movl 4(%eax), %eax /* recover return address */ /* Push the return value back. Doing this instead of just jumping to %eax preserves the cached call-return stack used by most modern processors. */ pushl %eax ret
#define MAXNODE 20000 ... void emit_order() { ... hrr_class hrr_nodes[MAXNODE]; /* Stack of HRR nodes */ vrr_class vrr_nodes[MAXNODE]; /* Stack of VRR nodes */
上に示した長いauto変数の配列をスタックに確保している2行を #ifndef __CYGWIN__ ... #endif
で括り,代わりに
#ifdef __CYGWIN__ hrr_class *hrr_nodes = (hrr_class *)malloc(sizeof(hrr_class)*MAXNODE); vrr_class *vrr_nodes = (vrr_class *)malloc(sizeof(vrr_class)*MAXNODE); #endif
をその下に追加,さらに関数末尾(return;
直前)に
free(hrr_class); free(vrr_class);
を追加. emit_deriv{1,12}_managers.c
でも同様にする(略).これで問題が回避できた(これらはPSI3が使う複雑な電子相関積分等を行うC/C++ソースを生成するためのものらしい).