gdb が解析できないスタック トレースを手動で解釈できるように、OCaml 呼び出し規約を見つけようとしています。残念ながら、一般的な観察以外に英語で書かれたものはないようです。たとえば、人々は OCaml が多くの引数をレジスタで渡すブログにコメントするでしょう。(どこかに英語のドキュメントがある場合は、リンクをいただければ幸いです。)
だから私は ocamlopt ソースからそれをパズルしようとしてきました。誰かがこれらの推測の正確性を確認できますか?
そして、最初の 10 個の引数がレジスタに渡されることについて私が正しければ、関数呼び出しに引数を復元することは一般的に不可能ですか? C では、正しいフレームに戻れば、引数はスタックのどこかにプッシュされます。OCaml では、呼び出し先は呼び出し元の引数を自由に破棄できるように見えます。
レジスタ割り当て(から/asmcomp/amd64/proc.ml
)
OCaml 関数を呼び出すには、
- 最初の 10 個の整数およびポインター引数は、レジスター rax、rbx、rdi、rsi、rdx、rcx、r8、r9、r10、および r11 に渡されます。
- 最初の 10 個の浮動小数点引数はレジスタ xmm0 ~ xmm9 で渡されます。
- 追加の引数がスタックにプッシュされ ( leftmost-first-in? )、float と int とポインターが混在します。
- トラップ ポインター (以下の「例外」を参照) は r14 で渡されます。
- 割り当てポインター (おそらく、このブログ投稿で説明されているマイナー ヒープ用) は r15 で渡されます。
- 戻り値は、整数またはポインターの場合は rax に返され、浮動小数点の場合は xmm0 に返されます。
- すべてのレジスタは呼び出し元保存ですか?
C 関数の呼び出しには、標準の amd64 C 規則が使用されます。
- 最初の 6 つの整数およびポインター引数は、rdi、rsi、rdx、rcs、r8、および r9 で渡されます。
- 最初の 8 つの float 引数は xmm0 から xmm7 で渡されます
- 追加の引数はスタックにプッシュされます
- 戻り値はraxまたはxmm0で返されます
- レジスタ rbx、rbp、および r12 ~ r15 は callee-save です。
返送先住所(から/asmcomp/amd64/emit.mlp
)
戻りアドレスは、amd64 C 規則に従って、呼び出しフレームにプッシュされた最初のポインターです。(私は、ret
命令がこのレイアウトを想定していると推測しています。)
例外(から/asmcomp/linearize.ml
)
コードtry (...body...) with (...handler...); (...rest...)
は次のように線形化されます。
Lsetuptrap .body
(...handler...)
Lbranch .join
Llabel .body
Lpushtrap
(...body...)
Lpoptrap
Llabel .join
(...rest...)
次に、このようなアセンブリとして出力されます (右側の宛先):
call .body
(...handler...)
jmp .join
.body:
pushq %r14
movq %rsp, %r14
(...body...)
popq %r14
addq %rsp, 8
.join:
(...rest...)
Lraise
本体のどこかに、この正確なアセンブリとして出力される線形化されたオペコードがあります。
movq %r14, %rsp
popq %r14
ret
これは本当にきれいです!この setjmp/longjmp ビジネスの代わりに、戻りアドレスが例外ハンドラーであり、唯一のローカルが前のダミー フレームであるダミー フレームを作成します。/asmcomp/amd64/proc.ml
$r14 を「トラップ ポインタ」と呼ぶコメントがあるので、このダミー フレームをトラップ フレームと呼びます。例外を発生させたい場合は、スタック ポインタを最新のトラップ フレームに設定し、トラップ ポインタをその前のトラップ フレームに設定してから、例外ハンドラに「戻ります」。そして、例外ハンドラーがこの例外を処理できない場合は、それを再発生させるだけです。
例外は %eax にあります。