2016-06-21 7 views
5

私は、次の疑問があります。我々は、System Vのx86-64のABIはとてもレッドゾーンと呼ばれる、スタックフレームで固定サイズのエリア(128バイト)についての私達を与える知っているよう赤いゾーンがあるときにスタック割り当てが必要なのはなぜですか?

を。 その結果、たとえばsub rsp, 12を使用する必要はありません。ちょうどmov [rsp-12], Xとそれだけです。

しかし、私はそれの考えを把握することはできません。なぜそれは重要ですか?レッドゾーンなしでsub rsp, 12が必要ですか?結局のところ、スタックのサイズは最初は制限されているので、なぜsub rsp, 12が重要ですか?私はそれがスタックの上をたどることを可能にするが、それを無視することを可能にすることを知っている。

rspの値(retなど)の使用方法はわかりますが、その瞬間は気にしないでください。

問題の核心がある: 私たちは何のレッドゾーンを持っていないし、私たちがやった:

function: 
    mov [rsp-16], rcx 
    mov [rsp-32], rcx 
    mov [rsp-128], rcx 
    mov [rsp-1024], rcx 
    ret 

それはとの違いですか?

function: 
    sub rsp, 1024 
    mov [rsp-16], rcx 
    mov [rsp-32], rcx 
    mov [rsp-128], rcx 
    mov [rsp-1024], rcx 
    add rsp, 1024 
    ret 
+0

ここに示した2番目のコードスニペットは間違っています。スタックポインタを減らす場合は、関数から戻る前にスタックポインタを復元する必要があります。したがって、 'ret rpの前に' add rsp、1024'を追加する必要があります。 –

+0

これはどれですか?私はLinuxを想定していますが、他にもLinuxがあります。 Windows 64、Mac OS X 64ビットなど –

+1

@rudy私が理解する限り、x8664 ABIは2つしかありません:System V AMD64 ABI(Linux、Solaris、OS X、その他のPOSIXで使用されています)準拠のオペレーティングシステム)、およびMicrosoftの実装はWindows上で使用されます。問題は前者の問題だと思われる。 –

答えて

9

「赤いゾーン」は厳密には必要ではありません。あなたの言葉では、それは「無意味」と考えられます。あなたが赤いゾーンを使ってできることはすべて、IA-32 ABIをターゲットにした伝統的なやり方をすることもできます。ここで

は何AMD64 ABIが「レッドゾーン」について述べています:%rspで指し示さを超え

128バイトの領域に確保されていると考えられると信号によって変更または割り込みハンドラしてはなりません。したがって、関数は、関数呼び出しの間に必要とされない一時的なデータにこの領域を使用することがあります。特に、リーフ関数は、プロローグとエピローグでスタックポインタを調整するのではなく、スタックフレーム全体にこの領域を使用することがあります。このエリアは赤いゾーンとして知られています。

レッドゾーンの真の目的は、最適化ようです。その存在は、rsp以下の128バイトが信号または割り込みハンドラによって非同期的に拘束されないことをコードが想定することを可能にし、それをスクラッチ空間として使用することを可能にする。これにより、スタックポインタをrspに移動することによって、スタック上にスクラッチスペースを明示的に作成する必要がなくなります。 rspを減らして復元する命令を省略できるので、時間と空間を節約できるので、これは最適化です。

そうです、あなた AMD64でこれを行うことができます(およびIA-32でそれを行う必要があるでしょう)しながら:

function: 
    push rbp      ; standard "prologue" to save the 
    mov rbp, rsp     ; original value of rsp 

    sub rsp, 32     ; reserve scratch area on stack 
    mov QWORD PTR [rsp], rcx ; copy rcx into our scratch area 
    mov QWORD PTR [rsp+8], rdx ; copy rdx into our scratch area 

    ; ...do something that clobbers rcx and rdx... 

    mov rcx, [rsp]    ; retrieve original value of rcx from our scratch area 
    mov rdx, [rsp+8]    ; retrieve original value of rdx from our scratch area 
    add rsp, 32     ; give back the stack space we used as scratch area 

    pop rbp      ; standard "epilogue" to restore rsp 
    ret 

我々は我々のケースでそれを行うにはない必要を行います我々はスクラッチ領域として赤いゾーンを使用することができるので、128バイトのスクラッチ領域(またはそれ以下)しか必要ありません。私たちは、もはや(プロローグとエピローグに)rbpを保存し、復元することが不要になって、また解放、我々は(代わりにrbpの)ベースポインタとしてrspを使用することができ、スタックポインタをデクリメントする必要があるため

プラス、別の汎用レジスタとして使用するためのrbpをアップしてください! 、ABIが許可しているため、デフォルトで-O1を有効にすると、プロローグセクションとエピローグセクションが同じ利点で削除される可能性がありますが、赤いゾーンがなければ、スタックポインタを調整してスペースを確保する必要はありません)。

ただし、ABIは信号や割り込みハンドラのような非同期のものは赤いゾーンを変更しないことを保証しています。他の関数を呼び出すと、赤いゾーンの値が壊れる可能性があるので、リーフ関数(関数呼び出しツリーの "leaf"にあるかのように、他の関数を呼び出さない関数) 。


最終点:Windows x64 ABIdeviates slightly from the AMD64 ABI used on other operating systems。特に、「赤いゾーン」という概念はありません。 rspを超える領域は揮発性とみなされ、いつでも上書きされる可能性があります。その代わりに、呼び出し元がhome address spaceをスタックに割り当てる必要があります。これは、レジスタが渡されたパラメータのいずれかを流出させる必要がある場合に、呼び出し先が使用できるようになります。

+0

さて、それは明らかです。だから私は、私たちのプロセスのシグナル/割り込みハンドラが(実際にはOSがそれを与える) 'rsp'を取ってそれを使用することを理解しています。そして、実際には 'rsp'の下に何かがあるときに問題を作り出すことができます。明らかに、それはレッドゾーンのない状況です。うん? – Gilgamesz

+1

シグナルまたは割り込みハンドラ*は赤いゾーンを使用できません。それはABIによって保証されています。 –

3

あなたの例ではオフセットが間違っているので、意味がありません。コードは以下の領域にはアクセスしないでください。スタックポインタは未定義です。レッドゾーンは、最初の128バイトをの下に置き、スタックポインタを以下に保護します。あなたの第二の例は次のようになります。

function: 
    sub rsp, 1024 
    mov [rsp+16], rcx 
    mov [rsp+32], rcx 
    mov [rsp+128], rcx 
    mov [rsp+1016], rcx 
    add rsp, 1024 
    ret 

を機能が必要であることをスクラッチ・スペースの量は、128のバイトまでであれば、それはスタックを調整することなく、以下のスタックポインタのアドレスを使用することができます。これは、最適化され。比較:

function:  // Not using red-zone. 
    sub rsp, 128 
    mov [rsp+120], rcx 
    add rsp, 128 
    ret 

レッドゾーンを使用して同じコードで:

function:  // Using the red-zone, no adjustment of stack 
    mov [rsp-8], rcx 
    ret 

コンパイラは、フレーム(RBPから負のオフセットを生成するので、スタックポインタからのオフセットについて混乱が正常に引き起こされます)、スタックからの正のオフセット(RSP)ではありません。

+1

'-fomit-frame-pointer'は、Linuxをターゲットとするgccの' -O1'以上のデフォルトであり、何年も続いています。あなたは通常 '-Ob'出力の地方の' rbp'からのオフセットしか見ませんが、それは見た目が面白くありません。楽しい事実: '-128'が' rsp'からの最大1バイトの移動であるため、赤いゾーンのサイズが選択されました。 –

+0

私はそれがデフォルトになったのか分かりませんでした。私は私が見ているよりも古くなっています:) – Amoss

+0

@Peter、なぜ255バイト、符号なしバイトとして255の最大値を取ることができますか? –

関連する問題