13

本当に奇妙な gcc 癖。これをチェックしてください:

main() { int a[100]; a[0]=1; }

このアセンブリを生成します:

   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   48 81 ec 18 01 00 00    sub    $0x118,%rsp
   b:   c7 85 70 fe ff ff 01    movl   $0x1,-0x190(%rbp)
  12:   00 00 00 
  15:   c9                      leaveq 
  16:   c3                      retq

100 * 4 の配列であるため、スタックのトップは明らかに 400 です。したがって、最初のエントリに書き込むときは、rbp - 400 (行 'b') を実行します。良い。しかし、スタック (行 '4') ポインターから 280 を減算するのはなぜですか? それは配列の真ん中を指していませんか?

後で関数呼び出しを追加すると、gcc は正しいことを行います。

b() {}
main() { int a[100]; a[0]=1; b(); }

このアセンブリを生成します:

0000000000000000 <b>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   c9                      leaveq 
   5:   c3                      retq   

0000000000000006 <main>:
   6:   55                      push   %rbp
   7:   48 89 e5                mov    %rsp,%rbp
   a:   48 81 ec 90 01 00 00    sub    $0x190,%rsp
  11:   c7 85 70 fe ff ff 01    movl   $0x1,-0x190(%rbp)
  18:   00 00 00 
  1b:   b8 00 00 00 00          mov    $0x0,%eax
  20:   e8 00 00 00 00          callq  25 <main+0x1f>
  25:   c9                      leaveq 
  26:   c3                      retq 

ここでは、適切に 400 を減算します (行 'a')。

関数呼び出しを追加すると、なぜ変化するのですか? gcc は単に怠け者であり、それが問題ではないために正しく機能しないのでしょうか? 何が起こっていますか?明らかに、これは x86_64 用にコンパイルする場合にのみ発生しますが、プレーンな x86 では発生しません。これは x86_64 の「レッドゾーン」と関係がありますか? 正確に何が起こっているのですか?

4

2 に答える 2

14

あなたの推測は正しいです。「レッドゾーン」です。レッド ゾーンは、rsp-128 から rsp までのスペースで、関数によってローカル変数および一時ストレージとして使用される可能性があります。このスペースは、割り込みおよび例外ハンドラによって変更されません。明らかに、レッド ゾーンは関数呼び出しによって破棄されるため、関数が呼び出された場合、ローカル変数はレッド ゾーンに入ることができません。

レッド ゾーンは、64 ビットの Linux、BSD、および Mac でのみ使用できます。カーネル コードでは使用できません。

レッド ゾーンを使用すると、rsp と ebp のみに基づく短い命令で最大 512 バイトのローカル変数を参照できるため、スペースの最適化に使用できます。レッド ゾーンがないと、384 バイトしか使用できません。この制限外のすべてのローカル変数には、より長いコードまたは追加のレジスタを使用してアクセスします。

あなたの例では、レッドゾーンを使用する必要はありませんが、gcc はすべての「リーフ」関数に使用することを好みます。この方法でコンパイラを実装する方が簡単です。

于 2011-12-01T13:08:22.327 に答える
5

x86-64 ABI は、変更せずに使用できるスタック ポインターを超える 128 バイトの「レッド ゾーン」を義務付けています%rsp。最初の例でmain()は、 はリーフ関数であるため、コンパイラはスタック スペースの使用を最適化しています。つまり、関数呼び出しがないため、この領域は上書きされません。

于 2011-12-01T13:09:01.830 に答える