Explore "Full-Stack" in depth!

情報系の専門学校で、今は機械学習に的を絞って学習中。プログラミングを趣味でやりつつ、IT系のあらゆる知識と技術を身に付けるべく奮闘中。

IA-32 Architectures Developer's Manual 3章まとめ

目次

概要

IA-32 マニュアルを読み始めました。
x86ニーモニックをバイナリに変換するアセンブラ自作をやる上で、
x86アーキテクチャの理解が必要だと思ったからです。

無料で読めるドキュメントとしては破格の詳しさなので、
システムプログラミングに興味のある人は全員読むべきです。

今回はこのマニュアルのうち3章の内容をベースにしながら、
自身で調べたことをまとめて紹介していこうと思います。


動作モード

IA-32アーキテクチャでは次の動作モードをサポートしています。
動作モードによって 命令セットやアーキテクチャ上の利用できる機能に違いが生まれます。

保護モード・プロテクトモード

x86アーキテクチャの動作モードの一つです。
プロセッサ本来の動作状態を示します。

全ての命令・アーキテクチャ上の機能が利用可能で、
最高の処理能力と機能が担保されています。

プロテクトモードをサポートするプロセッサであっても、
電源投入直後リセット直後はリアルモードで動作します。

その後はプロテクトモードで動作します。

プロテクトモードは Intel 80286で実装され、
その後 Intel 80386 によって多数機能が追加されました。

  • ページング方式
  • 32ビット長の物理アドレス空間及び仮想アドレス空間
  • 32ビットのセグメントオフセット
  • リアルモードへの復帰
    • 386以前はプロテクトモードからリアルモードへ復帰する方法が提供されていなかった
  • 仮想86モード

後述する実アドレスモードの8086ソフトウェアを保護されたマルチタスク環境で直接実行出来、
これを 仮想8086モード とか呼んだりします。

簡単に言うとプロテクトモードで リアルモード(後述) と似た挙動を再現するものですね。

実アドレスモード・リアルモード

先程言ったように、
IA-32アーキテクチャ電源投入直後リセット直後にリアルモードに移行します。

x86アーキテクチャにおいてBIOSは全てこのモードで動作します。

全てのレジスタのアドレス幅は16ビット長であり、
アドレッシングの際にはセグメントレジスタの値を16倍して加える事で20ビット長のアドレス空間にアクセスすることが可能。

プロテクトモードと違い メモリに対するアクセス保護機能がありません。

SMM( System Management Mode)

3つ目の動作モードです。
電源管理やシステムセキュリティ等 プラットフォーム固有の機能 を実装するための機能を提供します。
外部SMM割り込みピンがアクティブになるか、
APIC( Active Programmable Interrupt Controller )からSMIを受け取るとSMMに移行します。

APICx86アーキテクチャに置ける割り込みコントローラです。
参考:APIC

またSMIは システムマネジメント割り込み と呼ばれる外部割り込みです。 最高の優先度を持ったハードウェア割り込みになっています。

IA-32における基本実行環境

基本実行環境とは、
プログラムを記述する利用者側が使えるマシンリソースと言えます。

このマシンリソースには 論理的なものも含まれます。
後述しますが、 スタックがいい例です。

各項目については次節移行で詳しく解説します。
ここでは いろんな概念・データ構造を利用出来る ということだけ知っておきましょう。

アドレス空間

IA-32プロセッサ上で実行されるプログラム・タスクに与えられます。
最大 4ギガバイトリニアアドレス空間と、
最大 64ギガバイト物理アドレス空間 を指定出来ます。

基本プログラム実行レジスタ

一連の汎用命令を実行する為の実行環境です。

本節を執筆するに当たりこちらの記事)を全面的に引用させていただきました。

まず、
8個の汎用レジスタが存在します。
これは32ビット長であり、

EAX/AX/AL/AH : アキュムレータレジスタ。算術演算操作の結果が格納される。
ECX/CX/CL/CH : カウンタレジスタ。シフトローテート命令とループ命令に使用される。
EDX/DX/DL/DH : データレジスタ。算術演算操作とI/O操作に使用される。
EBX/BX/BL/BH : ベースレジスタ。セグメントモードでのデータセグメントに格納されたデータを指し示すために使用される。
ESP/SP : スタックポインターレジスタ。スタックにおいて次に値を格納する位置を示すポインタ。
EBP/BP : スタックベースポインタレジスタ。スタックの底を指し示すのに使用される。
ESI/SI : ソースレジスタ。ストリーム操作でのソースへのポインタとして使用される。
EDI/DI : デスティネーションレジスタ。ストリーム操作でのデスティネーションへのポインタとして使用される。

が存在します。

因みにレジスタ名に入っている EXはどちらも Expand を表しています。
アキュムレータレジスタを例に取ると、

  • AHレジスタにおいて高位アドレス側の8ビットにアクセス
  • AL …同じく低位アドレス側の8ビットにアクセス
  • AX …16ビットアクセス
  • EAX …32ビットアクセス

となっています。

汎用レジスタは以下の値を保持します。

これらの他にも、
特定の用途によってレジスタを用いる場合があります。

例えば ESP( Expanded Stack Pointer )レジスタがそのいい例ですよね。
スタックのトップを指すポインタ以外の用途は原則として避けなければなりません。

その他にもストリング操作命令 (movsとかcmpsとか)にはEXC,ESI,EDIを用いるなど、
特定のレジスタを割り当てる場合もあります。

次に6個のセグメントレジスタが存在します。
16ビット長であり、

SS : スタックセグメント。スタックへのポインタ。
CS : コードセグメント。コードへのポインタ。
DS : データセグメント。データへのポインタ。
ES : エクストラセグメント。追加のデータへのポインタ。(EはExtraのEである)
FS : Fセグメント。さらに追加のデータへのポインタ。(FはEの次である)
 GS : Gセグメント。さらにさらに追加のデータへのポインタ。(GはFの次である)

のようなものが挙げられます。

これらは16ビットのセグメント・セレクタを保持します。
セグメントセレクタは後述しますが、
メモリ上のセグメントを識別するための値であり、
論理アドレスに含有されています。

まず、実行プログラムはそれぞれセグメントに分けられています。

この内、

  • コードセグメント…実行中(またはされる)命令を格納する領域
  • データセグメント…プログラムが利用するデータを保有する領域
  • スタックセグメント…プログラムが利用するスタック構造を保有する領域

実際にデータが置かれているのは物理メモリ上なので、
物理メモリを指すリニアアドレスを保有する領域が適切です。

があります。
ES.FS,GSはそれぞれ追加のデータセグメントを示すので、
大別すると上記の3つであることがわかります。

後述しますが、命令ポインタ( EIP )はコードセグメント上の命令を指しています。
このコードセグメントはセグメントレジスタである CSが示すアドレスから始まります。

操作結果やプロセッサ状態を示す32ビット長レジスタとして、
EFLAGSレジスタが存在します。
各ビットに意味があり、

0.   CF : キャリーフラグ。最後の算術演算操作で加算においてレジスタの大きさを越えてビットのキャリー(桁上がり)かボロー(桁借り)をした場合にセットされる。これは、キャリーの生じた加算やボローの生じた減算の次の操作がされた場合に、1個のレジスタだけ扱うことのできる値であるか確認するのに使われる。
2.  PF : パリティフラグ。結果の最下位バイトに値1 のビットが偶数個含まれている場合にセットされ、奇数個の場合にはクリアされる。
4.  AF : 調整フラグ。2進化10進 (BCD) 演算の算術演算でキャリーまたはボローが生じたらセットされる。
6.  ZF : ゼロフラグ。操作の結果がゼロ (0) になった場合にセットされる。
7.  SF : 符号フラグ。操作の結果が負となった場合にセットされる。
8.  TF : トラップフラグ。ステップバイステップのデバッギングをする場合にセットする。
9.  IF : 割り込み可能フラグ。割り込みを有効化したい場合にセットする。
10.     DF : 方向フラグ。ストリームの方向を制御する。セットするとストリング命令においてポインタがデクリメントされる(通常はインクリメントされる)。
11.     OF : オーバーフローフラグ。符号付き算術演算の結果がレジスタに格納できないほど大きい値になった場合にセットされる。
12-13.  IOPL : I/O特権レベルフィールド(2ビット)。現在のプロセスのI/O特権レベルを示す。
14.     NT : ネストタスクフラグ。割り込みチェーンを制御する。現在のプロセスが次のプロセスにリンクされている場合にセットされる。
16.     RF : 再開フラグ。デバッグ例外への応答を制御する。
17.     VM : 仮想8086モード。8086互換モードにある場合にセットされる。
18.     AC : アラインメントチェックフラグ。メモリチェックでアラインメントチェックが有効な場合にセットされる。
19.     VIF : 仮想割り込みフラグ。IFの仮想イメージ。
20.     VIP : 仮想割り込み保留フラグ。割り込みが保留されている場合にセットされる。
21.     ID : 識別フラグ。セットできればCPUID命令がサポートされる。 

のように定義されています。

また有名なものに EIP(命令ポインタ)があります。
これは call命令の直後のみ読まれ、
次に実行する命令のアドレスを保持しています。
EIPは 32ビット長 です。

基本プログラム実行レジスタ以外の実行環境を簡単に紹介します。
気になった方は調べてみてください。


メモリ構成

プロセッサがバス上でアドレス指定するメモリは物理メモリと呼ばれ、
8ビットのバイトシーケンスとして構成されます。

バスはIT技術で広く用いられる用語ですが、 ここでは CPUバスの意で用いています。

CPUバスの構成要素を述べます。

  • クロック…電圧の高低を周期的に取る信号である
  • 制御信号
  • アドレスバス…CPU等がアクセスしたい(メモリ上の)バイトの要素/位置を伝えるために使用するバス。
    • バスの幅(本数の意)によってアドレッシング可能なアドレス長・メモリサイズが異なる。
  • データバス…CPUとメモリ・IOユニット間でデータ転送するために用いられる。
    • 基本的には双方向通信を実現する。

物理メモリの各バイトには物理アドレスと呼ばれるアドレスが割り当てられます。
物理アドレス空間は0から最大64GBの範囲を取ります。

プロセッサのメモリ管理機能を利用する場合、
実行プログラムが物理メモリを直接アドレッシングすることはまずありません。

  • フラットメモリモデル
  • セグメント化モデル
  • 実アドレスモード

のいずれかの方法でメモリアクセスを行います。
ここでは実アドレスモードについては扱わないので、
IA-32 Intel® Architecture Software Developers Manual 下巻を参照してください。

フラットメモリモデル

プログラムからはリニアアドレス空間という単一の連続したアドレス空間を参照させ、
リニアアドレスと物理アドレスを変換する機構(ページング機構)を用いてメモリアクセスを実現する方式です。

リニアアドレス空間最大4GBの範囲を持ちます。

セグメント化メモリモデル

プログラムからはセグメントと呼ばれるアドレス空間を利用させ、
プログラム(Linuxではプロセス)に割り当てた論理アドレス(空間)でセグメントにアクセス出来るようにしています。

論理アドレスセグメントセレクタオフセットという値を持ち、
セグメント・セレクタでセグメントを識別し、付属するオフセットで特定のバイトを識別しています。

内部では定義されたセグメントが全てリニアアドレス空間マッピングされるため、
プログラムは論理アドレスを用いて物理メモリの資源にアクセスしている 「ように見え」ます。

セグメント化モデルの大きなメリットとしては、
プログラムやシステムの信頼性が向上することです。

C言語のプログラミングをしていて Segmentation faultというメッセージが表示された事はあると思います。

ELFバイナリには 各 セグメント の情報がプログラムヘッダテーブルに記載されていますが、
各セグメントには フラグ と言って、セグメントに対して許されている処理が設定されています。

プログラムヘッダテーブルについて知りたい方は下記記事を御覧ください。
というか皆さん readelf作ってみましょう!

drumato.hatenablog.com

例えばスタック領域には実行権限がありません。
なのでオプションをつけない限り スタック上に展開されたシェルコードが実行される事もありませんよね。

C言語のプログラムがセグフォで落ちるのは、
書き込み権限の無いセグメントにプログラムが書き込もうとしたからなのです。

このように、
セグメントごとに細かい設定が出来たりする事で、
セキュリティ性を高めていると言えそうですね。

プログラムに割り当てられた論理アドレスは、
セグメント機構によってリニアアドレスに変換されます。

リニアアドレスはページング機構を通して物理アドレスに変換されます。
ページング機構が無効の場合はリニアアドレスと物理アドレスが一対一に対応するというわけです。


動作モードとそれに対応するメモリ構成

はじめに述べた動作モードと、
対応するメモリモデルについて話していきます。

まず プロテクトモード は、上記に述べた任意のメモリモデルを利用できます。
どのメモリモデルを利用するかについては、
OSや実行プログラムの設計によって異なります。

次に実アドレスモードは実アドレスモードのメモリモデルしか利用できません。

最後にシステムマネージメントモードですが、
プロセッサは 独立したアドレス空間 を切り替えます。

システムマネジメント割り込みをきっかけに、
専用のRAMであるSMRAMマッピングしています。

命令ポインタ

EIPレジスタは、 call 命令を実行時に読み出されるレジスタで、
次に実行される命令用のオフセットを格納しています。

ソフトウェアから直接アクセスできない のが大きな特徴で、
JMP,CALL,`RET等の制御転送命令、割り込み、例外等によって、
暗黙的に制御されます。