2017-01-28 12 views
13

私は少しアセンブリを学びたいと思っています。コンピューターアーキテクチャークラスのために必要です。x86_64アセンブラのRBPレジスタの目的は何ですか?

pushq %rbp 
movq %rsp, %rbp 
subq $16, %rsp 

I:私はプログラムを書く時はいつでも、私はそれらの3本のラインを使用することを認識などフィボナッチ数列を、印刷するように(私はそれが同等Cだとgccから生成されたアセンブリコードを比較するから学んだとして)私は、いくつかのプログラムを書きましたそれについて2つの質問があります:

  1. まず、%rbpを使用する理由は何ですか。 %rspを使用する方が簡単ですか?その内容は2行目の%rbpに移動されますか?
  2. %rspから何かを差し引かなければならないのはなぜですか?私はprintf INGライン7または8の変数だったとき、私は私がVitrualマシン(4ギガバイトRAM)、Intelの64ビットプロセッサ上でManjaro 64ビットを使用し、その後、私は24または28

を引くウォルド、(それは常に16ではありません意味します

+0

最適化を有効にするのを忘れました。減算する量は、配置要件と赤いゾーンを使用できるかどうかによって異なります。 – Jester

+0

@Jester最適化を有効にしても、フレームポインタの省略が有効になるわけではありません。 –

+3

[基本ポインタとスタックポインタとはまったく同じですか?彼らは何を指しているのですか?](http://stackoverflow.com/questions/1395591/what-is-exactly-the-base-pointer-and-stack-pointer-to-what-do-theypoint)。それはx86_32コードと同じです。 – usr2564301

答えて

9

rbp x86_64版のフレームポインタである。調整はrsp(すなわちへのローカル変数やpush ING値のためのスペースを確保するに行われたときにように、あなたの生成されたコードでは、それはスタックポインタ(rsp)のスナップショットを取得しますスタック)、ローカル変数と関数パラメータは、からの一定のオフセットからアクセス可能です10。

多くのコンパイラは、最適化オプションとしてフレームポインタ省略を提供しています。これにより、生成されたアセンブリコードのアクセス変数がrspを基準にして行われ、関数内で使用する別の汎用レジスタとしてrbpが解放されます。

GCCの場合、私はあなたがAT & Tアセンブラ構文から使用していると推測していますが、そのスイッチはです。そのスイッチでコードをコンパイルして、どのアセンブリコードが得られるかを確認してください。 rbpの代わりにrspを基準にした値にアクセスすると、ポインタのオフセットが関数全体で変化することに気付くでしょう。

+1

正確には、 '%rsp'!を使用します。しかし、それは私が 'movl'最初の定数、f.e' movl $ 10'それを '4(%rsp)'に動かすときです。なぜ '-4'なの?とbtw、それはまだ '%rsp'からいくつかの値を引きますか?私はコメントを通じてそれを理解していませんでした – FrynioS

+0

@FrynioSコンパイラは、関数の入力にローカル値のためのスペースを割り当てます。それは、入力時に%rspから値を減算する理由です。これは、%rbpがフレームポインタとして使用されているかどうかには依存しません。その後、この場所は%rspに正のオフセットで使用されます。また、この関数が別の関数を呼び出す場合、%rspは呼び出しごとに16バイトの境界に揃うため、コンパイラは各入力時に%rspから8を減算します。 – Netch

+0

@FrynioS noticeまた、%rspの前に128バイトのスペース( "赤いゾーン")があり、その内容は関数呼び出しの間に保持されますが、割り込み中はOSによって保持されます。したがって、非常に一時的な値(関数呼び出し間)は、%rspに対する負のオフセットで使用できます。すべてのコンパイラがこれを利用するわけではありません。 – Netch

14

Linuxはx86-64(AMD64)アーキテクチャ用のSystem V ABIを使用します。詳細はSystem V ABI at OSDev Wikiを参照してください。

これは、スタックがになったことを意味します。;より小さいアドレスはスタック内で「上位」になります。典型的なC関数が

 pushq %rbp  ; Save address of previous stack frame 
     movq %rsp, %rbp ; Address of current stack frame 
     subq $16, %rsp ; Reserve 16 bytes for local variables 

     ; ... function ... 

     movq %rbp, %rsp ; \ equivalent to the 
     popq %rbp  ;/'leave' instruction 
     ret 

にコンパイルされているローカル変数のために予約されるメモリの量は、16バイトにアラインスタックを維持するために、常に16バイトの倍数です。ローカル変数にスタック領域が必要ない場合は、subq $16, %rspまたはそれに類する命令はありません。

(リターンアドレススタックにプッシュ前%rbpサイズは両方の8バイト、合計16バイトであることに留意されたい。)

一方の上部に現在のスタックフレームに%rbp点、%rsp点スタックコンパイラは%rbp%rspの差を関数内の任意の点で知っているため、ローカル変数のいずれかを自由に使用できます。

スタックフレームは、ローカル関数の遊び場です。現在の関数が使用するスタック領域です。

現在のバージョンのGCCでは、最適化が使用されるたびにスタックフレームが無効になります。 Cで書かれたプログラムの場合、スタックフレームはデバッグには最も役立ちますが、あまり多くはありません。

同じABIがすべてのバイナリに適用されますが、どの言語で書かれていても、特定の他の言語では、「巻き戻し」のためにスタックフレームが必要になります(たとえば、-O2 -fno-omit-frame-pointerを使用すると、スタックフレームを維持できます。 "(例えば、現在の関数の祖先呼び出し側に"例外をスロー "するため)。スタック上に不要なものを残すことなく、1つまたは複数の機能を中止し、いくつかの祖先関数に制御を渡すことができるスタックフレームを「巻き戻す」ことができる。スタックフレームが省略されている

から GCCのため - 、基本的

 subq $8, %rsp ; Re-align stack frame, and 
          ; reserve memory for local variables 

     ; ... function ... 

     addq $8, %rsp 
     ret 

の機能の実装の変更なしスタックフレームが存在しないので(%rbpは、他の目的のために使用され、その値が押されることはありません各関数呼び出しは8バイトの量のスタックへの戻りアドレスのみをプッシュするので、%rspから8を引いて16の倍数にしておく必要があります(一般に、 〜%rspは8の奇数倍です)。

関数のパラメータは、通常、レジスタに渡されます。 %xmm0%xmm7にレジスタ内の浮動小数点引数を指定して、詳細については、この回答の冒頭でABIのリンクを参照してください、しかし、短期では、整数型とポインタは、レジスタ%rdiに渡され、%rsi%rdx%rcx%r8、および%r9

repの代わりにrep retが表示される場合もあります。混乱しないでください。rep retは、retと全く同じことを意味します。 repという接頭辞は、文字列命令(繰り返し命令)で通常使用されますが、ret命令に適用されても何も実行されません。特定のAMDプロセッサの分岐予測器がret命令にジャンプするのを好まないのは、その代わりにrep retを使用することです。

最後に、スタックの先頭(%rsp未満の128バイト)の上にあるred zoneを省略しました。通常のスタックフレームの場合、デバッグを可能にするためにローカルのものがスタックフレーム内にあることが望ましいでしょう。 omit-stack-frameの場合、スタックアライメントの要件では、すでに%rspから8を引く必要があることを意味しています。

+0

うわー、おい、それは私を助けてくれました!答えをありがとう(残念:私は受け入れられた別の答えをマーク:D) – FrynioS

+0

@ FrynioS:心配しない、それはすべて良いです! Govind Parmarがあなたが述べた質問をしていたのに対し、これは「役に立つものがあるかもしれない背景情報」、 –

+0

@NominalAnimal誤植を修正してください。正しいオプションは '-fomit-frame-pointer'です。 – Netch

関連する問題