web-dev-qa-db-ja.com

スタックの割り当て、パディング、および配置

私は、コンパイラーがマシンコードを生成する方法、より具体的にはGCCがスタックを処理する方法をより深く理解しようとしています。そうすることで、私は単純なCプログラムを作成し、それらをアセンブリにコンパイルして、結果を理解するために最善を尽くしてきました。簡単なプログラムとそれが生成する出力は次のとおりです。

asmtest.c

void main() {
    char buffer[5];
}

asmtest.s

pushl   %ebp
movl    %esp, %ebp
subl    $24, %esp
leave
ret

私が困惑しているのは、24バイトがスタックに割り当てられている理由です。プロセッサがメモリをアドレス指定する方法のため、スタックは4刻みで割り当てる必要があることを知っていますが、この場合、スタックポインタは24バイトではなく8バイトだけ移動する必要があります。参考までに、17のバッファバイトは、40バイト移動するスタックポインタを生成し、バッファなしでスタックポインタ8を移動します。1〜16バイトのバッファは、ESP24バイトを移動します。

ここで、8バイトが必要な定数であると仮定すると(何のために必要ですか?)、これは、16バイトのチャンクで割り当てていることを意味します。なぜコンパイラはそのように整列するのでしょうか? x86_64プロセッサを使用していますが、64ビットのWordでも8バイトのアライメントしか必要ありません。なぜ不一致?

参考までに、gcc 4.0.1を使用して最適化を有効にしない10.5を実行しているMacでこれをコンパイルしています。

45
David

これは-mpreferred-stack-boundary=nによって制御されるgcc機能であり、コンパイラーはスタック上のアイテムを2^nに揃えようとします。 n2に変更した場合、スタックには8バイトしか割り当てられません。 nのデフォルト値は4です。つまり、16バイトの境界に揃えようとします。

「デフォルト」の8バイトがあり、次に24 = 8 + 16バイトがあるのは、スタックにleaveretの8バイトがすでに含まれているため、コンパイルされたコードは最初にスタックを8ずつ調整する必要があるためです。 2 ^ 4 = 16に揃えるためのバイト数。

47
laalto

SSExファミリの命令では、パックされた128ビットベクトルを16バイトにアラインする必要があります。そうしないと、セグメンテーション違反が発生してロード/ストアしようとします。つまりスタック上のSSE)で使用するために16バイトのベクトルを安全に渡す場合、スタックは常に16に整列されている必要があります。GCCはデフォルトでそれを考慮します。

12
stormsoul

このサイト を見つけました。これには、スタックが大きくなる理由について、ページの下部に適切な説明があります。コンセプトを64ビットマシンにスケールアップすると、表示されている内容が説明される場合があります。

3
Chris Arguin

LWNにはメモリアライメントに関する記事があります 、おもしろいと思うかもしれません。

2
J-16 SDiZ

Mac OS X/Darwin x86 ABIには、16バイトのスタックアライメントが必要です。これは、Linux、Win32、FreeBSDなどの他のx86プラットフォームには当てはまりません...

1
Ringding