x86メモリセグメンテーション

ウィキペディアから、無料の百科事典
ナビゲーションにジャンプ 検索にジャンプ

x86セグメント方式は、の実装を指すセグメント方式のIntelでのx86コンピュータの命令セット・アーキテクチャ。セグメンテーションは、上で紹介されたインテル8086のプログラムは、64 KB以上(65,536対処することを可能にするための方法として1978年に バイトのメモリを)。インテル80286はのためのサポートを追加1982年にセグメンテーションの第二のバージョンを導入し、仮想メモリメモリ保護を。この時点で、元のモデルの名前はリアルモードに変更され、新しいバージョンの名前はプロテクトモードに変更されましたx86-64で 2003年に導入されたアーキテクチャでは、64ビットモードでのセグメンテーションのサポートが大幅に廃止されました。

実モードと保護モードの両方で、システムは16ビットセグメントレジスタを使用して実際のメモリアドレスを導出します。リアルモードでは、レジスタCS、DS、SS、およびESは、現在使用されているプログラムコードセグメント(CS)、現在のデータセグメント(DS)、現在のスタックセグメント(SS)、およびプログラマによって決定された1つの追加セグメントを指します。 (ES)。インテル80386 1985年に導入は、ハードウェアによって定義されていない特定の用途で、二つの追加セグメントレジスタ、FSおよびGSを加算します。セグメントレジスタの使用方法は、2つのモードで異なります。[1]

セグメントの選択は通常、実行されている機能に応じてプロセッサによってデフォルト設定されます。命令は常にコードセグメントからフェッチされます。スタックのプッシュまたはポップ、またはスタックを参照するデータ参照は、スタックセグメントを使用します。データへの他のすべての参照は、データセグメントを使用します。追加のセグメントは、文字列操作(MOVSやCMPSなど)のデフォルトの宛先です。FSとGSには、ハードウェアによって割り当てられた用途はありません。命令フォーマットでは、オプションのセグメントプレフィックスバイトを使用できます。これを使用して、必要に応じて、選択した命令のデフォルトセグメントを上書きできます。[2]

リアルモード

リアルモードメモリ内の3つのセグメント(画像をクリックすると拡大します)。セグメント2とセグメント3の間に重複があります。ターコイズ領域のバイトは、両方のセグメントセレクタから使用できます。

リアルモードまたはV86モード、セグメントのサイズが1つの範囲であり得るバイト(16ビットのオフセットを使用して)65,536バイトまで。

セグメントレジスタの16ビットセグメントセレクタは、セグメントアドレスと呼ばれる線形20ビットアドレスの最上位16ビットとして解釈され、残りの4つの最下位ビットはすべてゼロです。セグメントアドレスは常に命令の16ビットオフセットに追加され、このモードの物理アドレスと同じ線形アドレスを生成します。たとえば、セグメント化されたアドレス06EFh:1234h(ここでは接尾辞「h」は16進数を意味します)には06EFhのセグメントセレクタがあり、06EF0hのセグメントアドレスを表し、それにオフセットが追加され、線形アドレス06EF0h + 1234h = 08124hが生成されます。

  0000 0110 1110 1111  0000 セグメント 16ビット、左に4ビットシフト(または0x10を掛けたもの)
+      0001 0010 0011 0100 オフセット 16ビット
                          
  0000 1000 0001 0010 0100 住所 20ビット

セグメントアドレスとオフセットの追加方法により、単一の線形アドレスを最大2 12 = 4096の異なるセグメント:オフセットペアにマッピングできますたとえば、線形アドレス08124hは、セグメント化されたアドレス06EFh:1234h、0812h:0004h、0000h:8124hなどを持つことができます。

これは、一意のアドレス指定スキームに慣れているプログラマーにとっては混乱を招く可能性がありますが、たとえば、複数のネストされたデータ構造をアドレス指定する場合などに有利に使用することもできます。リアルモードのセグメントは常に64ですが KB長く、実用的な効果には、セグメントではなく、すべてのセグメントよりも、64 KBよりも長くなることはできませんということだけでは必須長さは64KBです。リアルモードでは保護や特権の制限がないため、セグメントを64 KB未満に定義できたとしても、他のプログラムと同様に、セグメントの範囲内で調整および維持するのは完全にプログラム次第です。常に任意のメモリにアクセスします(セグメントセレクタを任意に設定して、監視なしでセグメントアドレスを変更できるため)。したがって、リアルモードは、セグメントごとに1〜65,536バイトの範囲の可変長を持ち、CPUによって強制されないものと同じように想像できます。

(線形アドレス、セグメント化されたアドレス、およびセグメントフィールドとオフセットフィールドの先行ゼロは、わかりやすくするためにここに示されています。通常は省略されています。)

リアルモードの有効な20ビットアドレス空間は、アドレス指定可能なメモリを2 20 バイト、つまり1,048,576バイト(1 MB)に制限します これは、正確に20個のアドレスピンを備えたIntel 8086(およびその後密接に関連する8088)のハードウェア設計から直接派生したものです。(どちらも40ピンDIPパッケージにパッケージ化されていました。アドレスラインが20本しかない場合でも、アドレスバスとデータバスは多重化され、限られたピン数内のすべてのアドレスラインとデータラインに適合しました。)

各セグメントは、段落と呼ばれる16バイトの倍数で始まります、線形(フラット)アドレス空間の先頭から。つまり、16バイト間隔です。すべてのセグメントの長さは64KBであるため、これは、セグメント間でオーバーラップが発生する方法と、線形メモリアドレス空間内の任意の場所に多くのセグメント:オフセットペアでアクセスできる理由を説明しています。線形アドレス空間におけるセグメントの開始の実際の位置は、セグメント×16で計算できます。セグメント値が0Ch(12)の場合、線形アドレス空間のC0h(192)に線形アドレスが与えられます。その後、アドレスオフセットをこの番号に追加できます。 0Ch:0Fh(12:15)はC0h + 0Fh = CFh(192 + 15 = 207)になり、CFh(207)は線形アドレスになります。このようなアドレス変換は、CPUのセグメンテーションユニットによって実行されます。最後のセグメントFFFFh(65535)は、線形アドレスFFFF0h(1048560)で始まり、20ビットアドレス空間の終わりの16バイト前にあります。最大65,536バイトのオフセットで、20ビット8088アドレス空間の終わりを超えて最大65,520(65536-16)バイトにアクセスできます。 8088では、これらのアドレスアクセスは、65535:16がアドレス0にアクセスし、65533:1000が線形アドレス空間のアドレス952にアクセスするように、アドレス空間の先頭にラップアラウンドされました。プログラマーによるこの機能の使用は、線形アドレス空間が20ビットを超えて拡張された後のCPU世代でのゲートA20の互換性の問題。

16ビットリアルモードでは、アプリケーションが複数のメモリセグメントを利用できるようにすること(1つの64Kセグメントで利用可能なメモリよりも多くのメモリにアクセスするため)は非常に複雑ですが、最小のツールを除くすべてのツールに必要な悪と見なされていました(これは、より少ないメモリで実行できます)。問題の根本は、メモリ範囲全体のフラットアドレス指定に適した適切なアドレス演算命令が利用できないことです。[要出典]複数の命令を適用することでフラットアドレス指定が可能ですが、プログラムの速度が低下します。

メモリモデルのコンセプトは、セグメントレジスタの設定から派生します。たとえば、小さなモデルCS = DS = SSでは、プログラムのコード、データ、およびスタックはすべて、単一の64KBセグメント内に含まれています。小さなメモリモデルDS = SSので、同じセグメント内の両方のデータとスタック常駐。CSは、最大64KBの異なるコードセグメントを指します。

プロテクトモード

ローカル記述子テーブルを使用したプロテクトモードメモリ内の3つのセグメント(画像をクリックすると拡大します)

80286プロテクトモード

80286保護モードは、 2プロセッサのアドレス空間を拡張する24バイト(16メガバイト)ではなく、シフト値を調整することによって。代わりに、16ビットセグメントレジスタには、オフセットが追加される24ビットベースアドレスを含むセグメント記述子のテーブルへのインデックスが含まれるようになりました。そう、結果の物理アドレスはもはや20ビットに切り捨てられます:古いソフトウェアをサポートするために、プロセッサは、「リアルモード」で、それは小さな違いけれどもがある8086のセグメント化アドレス指定のモデルを使用するモードを起動し、実際のモードポインタ(ただし、8086のポインタ)は、現在100000の間のアドレスを参照することができ16と10FFEF 16この約64キロバイトのメモリ領域は、高メモリ領域(HMA)と呼ばれ、DOSの新しいバージョンでは、これを使用して、使用可能な「コンベンショナル」メモリ(つまり、最初のMBを増やすことができますHMAを追加すると、合計アドレス空間は約1.06MBになります。80286はリアルモードアドレスを20ビットに切り捨てませんが、80286を含むシステムは、21番目のアドレスラインであるA20ラインをゲートオフすることにより、プロセッサ外部のハードウェアで切り捨てることができますIBM PC ATは、(元のソフトウェアとの完全な後方互換性のためにこれを行うためのハードウェアを提供するIBM PCおよびPC / XTモデル)、および「その後のすべてのように、AT-class "PCクローンも同様でした。

286プロテクトモードは、8086/88マシンを使用する大勢のユーザーを除外するため、ほとんど使用されませんでした。さらに、リアルモードで行われたように、メモリを64kセグメントに分割する必要がありました。この制限は、サイズが64kを超えるメモリポインタの使用を許可する32ビットCPUで回避できますが、セグメント制限フィールドは24ビット長しかないため、作成できる最大セグメントサイズは16MBです(ただし、ページングより多くのメモリを割り当てるために使用できます。個々のセグメントが16MBを超えることはできません)。この方法は、フラットメモリスペースを生成するためにWindows 3.xアプリケーションで一般的に使用されていましたが、OS自体はまだ16ビットであったため、32ビット命令でAPI呼び出しを行うことはできませんでした。したがって、API呼び出しを実行するすべてのコードを64kセグメントに配置する必要がありました。

286プロテクトモードが呼び出されると、ハードウェアリセットを実行する以外に終了できませんでした。上昇するIBMPC / AT標準に準拠するマシンは、標準化されたキーボードコントローラーを介してCPUへのリセットを装う可能性がありますが、これは大幅に遅くなりました。Windows 3.xは、CPUの割り込み処理メカニズムで意図的にトリプルフォールトトリガーすることにより、これらの問題の両方を回避しました。これにより、CPUはほぼ瞬時にリアルモードに戻ります。[3]

詳細なセグメンテーションユニットワークフロー

論理アドレスは、16ビットのセグメントセレクタ(13 + 1アドレスビットを提供)と16ビットのオフセットで構成されます。セグメントセレクタは、セグメントレジスタの1つに配置する必要があります。このセレクターは、2ビットの要求特権レベル(RPL)、1ビットのテーブルインジケーター(TI)、および13ビットのインデックスで構成されます。

特定の論理アドレスのアドレス変換を試行すると、プロセッサは、TI = 0の場合グローバルディスクリプタテーブルまたはTI = 1の場合ローカルディスクリプタテーブルから64ビットのセグメント記述子構造を読み取ります次に、特権チェックを実行します。

max(CPL、RPL)≤DPL

ここで、CPLは現在の特権レベル(CSレジスタの下位2ビットにあります)、RPLはセグメントセレクタから要求された特権レベル、DPLはセグメントの記述子特権レベル(記述子にあります)です。すべての特権レベルは0〜3の範囲の整数であり、最小の数値が最大の特権に対応します。

不等式がfalseの場合、プロセッサは一般保護(GP)違反を生成します。それ以外の場合、アドレス変換は続行されます。次に、プロセッサは32ビットまたは16ビットのオフセットを取得し、セグメント記述子で指定されたセグメント制限と比較します。それが大きい場合、GP違反が発生します。それ以外の場合、プロセッサは記述子で指定された24ビットセグメントベースをオフセットに追加し、線形物理アドレスを作成します。

セグメント記述子はセグメントレジスタの非表示部分にキャッシュされるため、特権チェックはセグメントレジスタがロードされたときにのみ実行されます。[要出典] [1]

80386プロテクトモード

インテル80386以降、保護モードは80286保護モードのセグメンテーション機構を保持するが、ページングユニットは、セグメンテーションユニットと物理バス間のアドレス変換の第2層として追加されました。また、重要なことに、アドレスオフセットは(16ビットではなく)32ビットであり、各セグメント記述子のセグメントベースも(24ビットではなく)32ビットです。セグメンテーションユニットの一般的な動作は、それ以外は変更されていません。ページングユニットは有効または無効にできます。無効の場合、操作は80286の場合と同じです。ページングユニットが有効の場合、セグメント内のアドレスは、80286の場合の物理アドレスではなく、仮想アドレスになります。つまり、セグメントの開始アドレス、オフセット、そして、2つを追加することによって導出された最後の32ビットアドレスは、ページングユニットが有効になっている場合、すべて仮想(または論理)アドレスです。セグメンテーションユニットがこれらの32ビット仮想アドレスを生成して検証するとき、有効なページングユニットは、最終的にこれらの仮想アドレスを物理アドレスに変換します。物理アドレスは32ビットです。386ですが物理アドレス拡張をサポートする新しいプロセッサでは大きくなる可能性があります

80386はまた、4つのセグメントレジスタ(CS、DS、ES、およびSS)の元のセットに、2つの新しい汎用データセグメントレジスタ、FSおよびGSを導入しました。

386 CPUは、CR0制御レジスタのビットをクリアすることでリアルモードに戻すことができますが、これはセキュリティと堅牢性を強化するための特権操作です。比較として、286は、たとえばトリプルフォールトまたは外部ハードウェアを使用して、プロセッサを強制的にリセットすることによってのみリアルモードに戻すことができました

その後の開発

x86-64のアーキテクチャは、長いモード(64ビットモード)でセグメンテーションを使用していません。 CS、SS、DS、およびESの4つのセグメントレジスタは強制的に0になり、制限は264になります。セグメントレジスタFSおよびGSは、ゼロ以外のベースアドレスを持つことができます。これにより、オペレーティングシステムはこれらのセグメントを特別な目的で使用できます。レガシーモードで使用されるグローバルディスクリプタテーブルメカニズムとは異なり、これらのセグメントのベースアドレスはモデル固有のレジスタに格納さます。 x86-64アーキテクチャはカーネルモードユーザーモードのベースアドレスを交換できる特別なSWAPGS命令をさらに提供します。

たとえば、x86-64上のMicrosoft Windowsは、GSセグメントを使用して、スレッド環境ブロックを指します。これは、スレッドの小さなデータ構造であり、例外処理、スレッドローカル変数、およびその他のスレッドごとの状態に関する情報が含まれています。同様に、LinuxカーネルはGSセグメントを使用してCPUごとのデータを格納します。

GS / FSは、gccスレッドローカルストレージおよびカナリアベースのスタックプロテクターでも使用されます。

実践

論理アドレスは、x86アセンブリ言語明示的に指定できます(例:(AT&T構文))。

movl $ 42、%fs:(%eax); RTLのM [fs:eax] <-42)に相当

またはIntel構文

mov  dword  [ fs eax ]、 42

ただし、セグメントレジスタは通常暗黙的に使用されます。

  • すべてのCPU命令は、CSレジスタに保持されているセグメントセレクタによって指定されたコードセグメントから暗黙的にフェッチされます
  • ほとんどのメモリ参照は、DSレジスタに保持されているセグメントセレクタによって指定されデータセグメントから取得されます。これらは、セグメントオーバーライドプレフィックスがメモリ参照を行う命令の前にある場合、ESレジスタに保持されているセグメントセレクタによって指定された追加のセグメントからも発生する可能性があります。すべてではありませんが、デフォルトでDSを使用するほとんどの命令は、ESオーバーライドプレフィックスを受け入れます。
  • プロセッサスタック参照は、暗黙的(プッシュおよびポップ命令など)または明示的((E)SPまたは(E)BPレジスタを使用したメモリアクセス)のいずれかで、SSレジスタに保持されているセグメントセレクタによって指定されたスタックセグメントを使用します
  • 文字列命令(例:stosmovs)は、データセグメントとともに、ESレジスタに保持されているセグメントセレクタによって指定された追加のセグメントも使用します。

x86-32プロセッサではセグメンテーションをオフにできません(これは64ビットモードにも当てはまりますが、説明の範囲を超えています)。そのため、多くの32ビットオペレーティングシステムは、すべてのセグメントのベースを0に設定することでフラットメモリモデルをシミュレートします。セグメンテーションをプログラムに対してニュートラルにするため。たとえば、Linuxカーネルは4つの汎用セグメントのみを設定します

名前 説明 ベース 制限 DPL
__KERNEL_CS カーネルコードセグメント 0 4 GiB 0
__KERNEL_DS カーネルデータセグメント 0 4 GiB 0
__USER_CS ユーザーコードセグメント 0 4 GiB 3
__USER_DS ユーザーデータセグメント 0 4 GiB 3

ベースはすべての場合で0に設定され、制限は4 GiBであるため、セグメンテーションユニットは、ページングユニットに到達する前にプログラムが発行するアドレスに影響を与えません(もちろん、これは80386以降のプロセッサを指します。以前のx86プロセッサにはページングユニットがないためです。)

現在のLinuxは、GSを使用してスレッドローカルストレージを指します

セグメントは、コード、データ、またはシステムセグメントのいずれかに定義できます。セグメントを読み取り専用、読み取り/書き込み、実行などにするための追加の許可ビットがあります。

プロテクトモードでは、コードはCS(コードセグメントセレクタ)を除くすべてのセグメントレジスタ常に変更できます。これは、プロセッサの現在の特権レベル(CPL)がCSレジスタの下位2ビットに格納されているためです。プロセッサの特権レベルを上げる(およびCSをリロードする)唯一の方法は、lcall(far呼び出し)およびint(割り込み)命令を使用することです。同様に、特権レベルを下げる(およびCSをリロードする)唯一の方法は、lret(ファーリターン)およびiret(割り込みリターン)命令を使用することです。リアルモードでは、コードはファージャンプを行う(または8086または8088で文書化されていない命令を使用する)ことによってCSレジスタを変更することもできます[4]。POP CS)。もちろん、リアルモードでは特権レベルはありません。すべてのプログラムは、すべてのメモリとすべてのCPU命令への絶対的なチェックされていないアクセスを持っています。

セグメンテーションの詳細についてはAMDまたはIntelのWebサイトで無料で入手できるIA-32のマニュアルを参照してください

注意事項と参考文献

  1. ^ a b 「Intel64およびIA-32アーキテクチャソフトウェア開発者マニュアル」、第3巻、「システムプログラミングガイド」、2011年発行、ページ「第3A 3-11巻」、本は次のように書かれています。すべてのセグメントレジスタには、「表示」部分と「非表示」部分があります。(非表示部分は「記述子キャッシュ」または「シャドウレジスタ」と呼ばれることもあります。)セグメントセレクタがセグメントレジスタの表示部分にロードされると、プロセッサはセグメントレジスタの非表示部分にもセグメントセレクターが指すセグメント記述子からのベースアドレス、セグメント制限、およびアクセス制御情報。セグメントレジスタにキャッシュされた情報(表示および非表示)により、プロセッサは、セグメント記述子からベースアドレスと制限を読み取るために余分なバスサイクルを必要とせずにアドレスを変換できます。「」
  2. ^ Intel Corporation(2004)。IA-32インテルアーキテクチャソフトウェア開発者マニュアル第1巻:基本アーキテクチャ (PDF)
  3. ^ 「DevBlogs」
  4. ^ POP CSは、次の命令をフェッチするために命令ポインタから計算される実効アドレスを即座に変更するため、細心の注意を払って使用する必要があり、有用性は限られています。一般的に、ファージャンプの方がはるかに便利です。の存在はPOP CS、8086および8088の4つのセグメントレジスタのPUSHおよびPOP命令オペコードのパターンに従うため、おそらく偶然です。

も参照してください

外部リンク