私は時々のような命令を持って解体プログラム参照:ESP変えずに、ESP-4でスタックしてEAXに格納スタックはスタックポインタの上にのみ保存されていますか?
mov %eax, -4(%esp)
を。
一般的には、スタックポインタを超えてデータをスタックに入れ、それらのデータを保存することができますか(具体的に行わない限り変更されません)。
また、これは使用するOSによって異なりますか?
私は時々のような命令を持って解体プログラム参照:ESP変えずに、ESP-4でスタックしてEAXに格納スタックはスタックポインタの上にのみ保存されていますか?
mov %eax, -4(%esp)
を。
一般的には、スタックポインタを超えてデータをスタックに入れ、それらのデータを保存することができますか(具体的に行わない限り変更されません)。
また、これは使用するOSによって異なりますか?
異なるOSが異なるABIを持っているため、どのOSを使用するかは重要です。 (意味するものがわからない場合は、x86タグwikiを参照してください)。
私はmov %eax, -4(%esp)
は正気ことができることを見ることができる2つの方法があります。Linux x32 ABI(32ビットポインタとロングモード)で
、通常のx86-64 ABIのよう128B red zoneがあります。コンパイラは、アドレスサイズプレフィックスを使用してコードを生成します。 4(%rdi)
はすべての場合に4(%edi)
と同じになります(折り返しなど)。残念ながら、gcc 5.3はスタック上のローカルに32ビットアドレッシングを使用しています。%rsp == 0
(16Bにアライメントする必要があるため)の場合にのみラップできます。
とにかく、void foo(void) { volatile int x = 10; }
はgcc 5.3 -O3 -mx32
on the Godbolt Compiler Explorerと
movl $10, -4(%esp)
/ret
にコンパイルされます。
割り込みを無効にして実行される(カーネル)コード。 DMA以外の非同期は起こりませんので、スタックメモリを壊すものはありません。 (x86にはNMIがあります:ノンマスカブル割り込みです.NMIのハンドラによっては、NMIがスタックポインタの下にメモリを壊す可能性があります)。
ユーザスペースでは、
Jesterはdwelchの答えに対するコメントで指摘しているように、スタックポインタの下のページは(もちろん非同期に)破棄することができます。一時的にたくさんのスタックを使用していても、それらのページは永遠に無駄になりません。 %esp
がページ境界にある場合、-4(%esp)
は別のページにあります。新しく割り当てられたスタックメモリのページでフォールトする代わりに、スタックポインタの下のマップされていないページへのアクセスがLinux上のセグメンテーションに変わります。
あなたがそうでなければ(例えばレッドゾーン)保証を持っている場合を除き、その後、あなたは%esp
以下のすべてが、すべての命令間にわたり走り書きされていることを前提としなければなりません。標準の32ビットABIには赤いゾーンがなく、Windowsの64ビットABIにも1つもありません。スタックの非同期的な使用(通常はLinuxのシグナルハンドラによる)はプログラム全体であり、コンパイラが現在のコンパイル単位から判断できるものではありません(たとえコンパイラが-4(%esp)
が(%esp)
)。
Linux x32 ABIは、x8664とも呼ばれるIA64(i386)ではなく、AMD64(x86-64)用の64ビットABIです。それは後に設計されて以来、通常のAMD64 ABIによく似ています。
Linuxカーネル自体は敬意を表しません(カーネル内の)赤いゾーンGCCは、カーネル自体をコンパイルするときにGCCを使用しないように言われているので、Linuxカーネルでは赤いゾーンが尊重されると期待してはいけません –
@MichaelPetch:あなたが赤いゾーンを持っているときとは別のケースで割り込みを無効にしています。 –
_ESP_の下の歴史的な脚注はバグでした。いくつかの状況下ではGCC 2.96。いくつかのソフトウェアプロジェクトを警戒していない人がいました。公式3.0がリリースされ、厄介なバグが修正されたときには、 –
EDIT
一部の人々は、アドレスがアップ上昇または下降の増加「を参照してください。」以来、あなたは上記および下記で何を意味するかわかりません。
しかし、それは問題ではありません。スタックがアドレスXで初期化され、現在Yにある場合、XとYの間のデータは保存されなければなりません(両端は含まれません)。いずれの側のメモリもフェア・ゲームです。
オペレーティングシステムではないコンパイラは、これが起こると、スタックポインタを移動して、その関数に必要なものをすべてカバーします。それが終わったら元に戻します。それぞれのネストした関数は、より多くのスタックを消費し、それぞれが少し戻ることを返します。
Yを超えて行く(私はYがスタックポインタであることを意味すると仮定します)は、OSによって異なります。シグナルや割り込みのような非同期のものは、その領域がフリーであると仮定しているかもしれません。スワッピングはそれを保存しないかもしれません。メモリはまだ割り当てられていなくてもよく、OSは障害(Linuxがそうであると考える)を考慮してもよい。 – Jester
そのターゲットのコンパイラはそれを知っていて、それを使用しようとはしません。いずれにしても、これらのレジスタ(isrなどのコンパイル済みのもの(オペレーティングシステム))を使用して複数の実行パスがあるときは、いつでもそうするのは賢明ではありません。 –
OSや呼び出し規約、運に依存します。 64ビットモードでは、sysv abiを使用している場合は128バイトの赤色のゾーンが得られるので、明示的な割り当てを行わずにアクセスすることができます。他のシステムでは、通常、すべての賭けはオフであり、スタックポインタの下でアクセスすべきではありません。それはセグメンテーションするかもしれません(マップされていないメモリに入ることが起こった場合)か、シグナルのようなものによってぎゅうちょになるかもしれません。 – Jester
迅速な対応をありがとう!だから私はちょうど大きな量でスタックポインタを引くと、そこにOSが触れない大きなスタックスペース(そこにも有用なデータを持っていないかもしれません)がありますか? – compostable
32ビットのLinuxコードでは、関数が必要とするローカルスタックデータの量を_ESP_から減算します。 64ビットのLinuxコードでは、_RSP_の現在の値より128バイト下で、_RSP_を移動せずに、関数が他の関数を呼び出さない限り、そのデータが壊れないようにすることができます。 Jester氏が指摘しているように、これは、[System V 64-bit ABI in section 3.2.2](http://www.x86-64.org/documentation/abi.pdf) –