2009-06-30 17 views
40

私は、コンパイラがマシンコードを生成する方法、具体的にはGCCがどのようにスタックを扱うのかを深く理解しようとしています。そうすることで、シンプルなCプログラムを作成し、アセンブリに組み込み、結果を理解するために最善を尽くしています。ここでは簡単なプログラムだと、それが生成する出力:スタック割り当て、パディング、アラインメント

asmtest.cを:

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

asmtest.s:24のバイトは、スタック用に割り当てられている理由

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

私には不可解だ何があります。私は、プロセッサがどのようにメモリをアドレス指定するかにより、スタックは4の増分で割り振られなければならないことを知っていますが、この場合、スタックポインタを24ではなく8バイトだけ移動する必要があります。バイトはスタック・ポインタを40バイト移動させ、スタック・ポインタを移動させるバッファは一切ありません。1〜16バイトのバッファが移動しますESP 24バイト。

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

は、私は、GCC 4.0.1と10.5を実行しているMacでこれをコンパイルすることだし、何の最適化が有効になっていません。

答えて

43

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

leaveretの8バイトがすでにスタックに含まれているため、「デフォルト」の8バイト、次に24 = 8 + 16バイトがあるので、コンパイルされたコードでは、 2^4 = 16。

+0

"push%ebp"は8バイト減少しましたか?プラスretの8バイトは、すでに16バイトで整列されているはずです。なぜこの用量のコンパイラがこの8バイトを必要とするのでしょうか? –

+1

ああ、私はそれを得た。これは32ビットのマシンです。ごめんなさい。これはretでなければなりません4バイト+ ebp 4バイト+アライメントされた8バイト+バッファ16 –

+1

現在のバージョンのi386とx86-64 System V ABIは16Bスタックアラインメント( 'call'命令の前)を必要とするので、関数は仮定できますそれ。歴史的に、i386 ABIは4Bアライメントしか必要としませんでした。 (ABIドキュメントへのリンクはhttps://stackoverflow.com/tags/x86/infoを参照)。 GCCは、他の関数を呼び出せないリーフ関数でも '%esp'を整列させたままにします。スペースを確保しなければならない時に、ここで起こっていることです。 –

3

私は、スタックが大きくなる可能性がある理由について、ページの下部にあるいくつかのまともな説明を有し、this siteを発見しました。コンセプトを64ビットマシンまで拡大し、あなたが見ているものを説明するかもしれません。最初の命令は、(64ビットと仮定して)スタックの%EBPの開始値をプッシュするため

-1

8つのバイトがあります。

+1

リターンアドレスとベースポインタなどの他のx86プラットフォーム上のケースではありません。 – dreamlax

11

命令のSSExファミリは、16バイトにアラインされる128ビットのベクトルを詰め必要です - それ以外の場合は、あなたがそれらを保存/ロードしようとセグメンテーション違反を取得します。私。スタック上でSSEで使用するために16バイトのベクトルを安全に渡すには、スタックを常に16に整列させておく必要があります。デフォルトでは、GCCがこれを考慮します。

+0

私は、あなたの答えが間違っていると主張する事に関して、経験があまりにも少ないかもしれません。しかし、その目的のために 'movupd'と同様の** u ** naligned命令を使用しないでください(_unaligned_パックデータのロード/格納)?あなたが理解しているところでは、アラインされていないデータに対して 'movapd'と同様の命令を使用しようとすると、誤った振る舞いをすることがありますが、アンアライメントされているデータは一般的には問題ではありません。 – andreee

1

Mac OS X/Darwin x86 ABIでは、16バイトのスタックアライメントが必要です。これは、両方のスタックにプッシュされているなどのLinux、Win32の、FreeBSDの...

+1

実際のABI要件は、スタックが関数呼び出しの境界で* 16バイト整列*することです。 –

+2

これは当てはまりますが、関数のプロローグ/エピローグはスタックポインタが変更される唯一の場所であるため、これは常に整列する必要があるということとほぼ同じです。 – Ringding

関連する問題