図11●x86およびx64の命令体系<br>図中の数値は16進数で,マシン語コードを表す。64ビット・モードでは,オペコードの前に,64ビット・オペランドを指定するためのREXプレフィックス(40H~4FH)のフィールドが追加された。このマシン語コードに割り当てられていた1バイトのINC命令とDEC命令は,64ビット・モードではコードが変更された。
図11●x86およびx64の命令体系<br>図中の数値は16進数で,マシン語コードを表す。64ビット・モードでは,オペコードの前に,64ビット・オペランドを指定するためのREXプレフィックス(40H~4FH)のフィールドが追加された。このマシン語コードに割り当てられていた1バイトのINC命令とDEC命令は,64ビット・モードではコードが変更された。
[画像のクリックで拡大表示]
図12●64ビットWindowsでアプリケーションが動作する仕組み&lt;br&gt;WOW64上に,あたかも32ビットWindowsが載っているような形になり,32ビットWindowsのシステム・ファイルをすべてそのまま備えている。ただし,gdi32.dllなど一部のDLLは呼び出し先がWOW64に変わるので,変更されている。
図12●64ビットWindowsでアプリケーションが動作する仕組み<br>WOW64上に,あたかも32ビットWindowsが載っているような形になり,32ビットWindowsのシステム・ファイルをすべてそのまま備えている。ただし,gdi32.dllなど一部のDLLは呼び出し先がWOW64に変わるので,変更されている。
[画像のクリックで拡大表示]
図13●64ビットWindowsのファイル・システム&lt;br&gt;32ビット・アプリケーション用と64ビット・アプリケーション用に,それぞれ別のフォルダが作られている。
図13●64ビットWindowsのファイル・システム<br>32ビット・アプリケーション用と64ビット・アプリケーション用に,それぞれ別のフォルダが作られている。
[画像のクリックで拡大表示]
図14●WOW64によるファイル・システムのリダイレクト&lt;br&gt;あらかじめ「C:\WINDOWS\SysWOW64\32ビット.txt」と「C:\WINDOWS\system32\64ビット.txt」の2つのファイルを作っておいた。(a)32ビットのメモ帳で「C:\WINDOWS\SysWOW64」にアクセスした場合。(b)32ビットのメモ帳で「C:\WINDOWS\system32」にアクセスした場合。表示される内容が「C:\WINDOWS\SysWOW64」と同じである。(c)64ビットのメモ帳で「C:\WINDOWS\system32」にアクセスした場合。
図14●WOW64によるファイル・システムのリダイレクト<br>あらかじめ「C:\WINDOWS\SysWOW64\32ビット.txt」と「C:\WINDOWS\system32\64ビット.txt」の2つのファイルを作っておいた。(a)32ビットのメモ帳で「C:\WINDOWS\SysWOW64」にアクセスした場合。(b)32ビットのメモ帳で「C:\WINDOWS\system32」にアクセスした場合。表示される内容が「C:\WINDOWS\SysWOW64」と同じである。(c)64ビットのメモ帳で「C:\WINDOWS\system32」にアクセスした場合。
[画像のクリックで拡大表示]
図15●WOW64によるレジストリのリダイレクト&lt;br&gt;あらかじめ「HKEY_LOCAL_MACHINE\SOFTWARE\サンプル64」と「HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\サンプル32」の2つのキーを作っておいた。(a)64ビットのregeditでアクセスした場合。(b)32ビットのregeditでアクセスした場合。HKEY_LOCAL_MACHINE\SOFTWARE以下の内容が,64ビットのregeditでアクセスしたときのHKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node以下の内容になっている。
図15●WOW64によるレジストリのリダイレクト<br>あらかじめ「HKEY_LOCAL_MACHINE\SOFTWARE\サンプル64」と「HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\サンプル32」の2つのキーを作っておいた。(a)64ビットのregeditでアクセスした場合。(b)32ビットのregeditでアクセスした場合。HKEY_LOCAL_MACHINE\SOFTWARE以下の内容が,64ビットのregeditでアクセスしたときのHKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node以下の内容になっている。
[画像のクリックで拡大表示]

マシン語の一部が32ビットと異なる

 32ビットのx86と互換性を保ちつつ,64ビットに拡張されたのがx64の64ビット・モードだが,マシン語コードに一部非互換な部分がある。それは,64ビット・モードで利用する拡張されたレジスタを指定するための「REXプレフィックス」が追加されたからである。

 x86の命令は,命令プレフィックス(オプション),オペコード,ModR/M(アドレス指定形式指定子),SIB(第2のアドレス指定バイト),ディスプレースメント,即値——の順に,必要なバイトが並べられた可変長の体系になっている(図11[拡大表示])。命令プレフィックスには,(1)リピート(ストリング命令と一緒に使用),(2)セグメント・オーバーライド(メモリー・アクセス時のセグメントを指定),(3)オペランド・サイズ・オーバーライド(16ビット・オペランドを使用するときに指定),(4)アドレス・サイズ・オーバーライド——の4種類があり,必要なときだけ付く。

 例えば,ESIレジスタの値に8を足した値が表すアドレス上の4バイトのデータをEAXレジスタに格納する「MOV EAX,[ESI+8]」という命令は,オペコードが8BH,ModR/Mが46H,ディスプレースメントが08Hで,「8BH 46H 08H」という3バイトのマシン語コードになる。

 「MOV EAX,[EBX+ESI+8]」という,アドレスを表す第2オペランドにベース・レジスタ(EBX)とインデックス・レジスタ(ESI)を指定したときは,同じMOV命令なのでオペコードは8BH,第2オペランドにベース・レジスタとインデックス・レジスタを指定したので,ModR/Mが44H,SIBが1EHとなり,第2引数に指定したディスプレースメント08Hが最後に付く。第1引数に16ビットのAXレジスタを指定すると,オペランド・サイズ・オーバーライド・プレフィックスの66Hが先頭に付き,続いてオペコードが8BH,ModR/Mが44H,SIBが1EH,ディスプレースメントが08Hとなる。

 これがx64の64ビット・モードでは,図11bのように,オペコードの前にREXプレフィックスのフィールドが追加される。REXプレフィックスは,64ビット・レジスタを扱うときに付ける。32/16/8ビット・レジスタを指定するときは付けない。

 上記と同じ命令の64ビット・モードでのマシン語コードは,アドレス・サイズ・オーバーライド・プレフィックスの67Hが付く以外は同じである(64ビット・モードのデフォルトのアドレス・サイズは64ビットだから)。また,第1引数に64ビット・レジスタRAXを指定すると,REXプレフィックス(この場合は48H)が付く。

 REXプレフィックスは48Hだけでなく,使用するレジスタの種類に応じて40H~4FHが割り当てられている。このコードは,x86(32ビット)では,1バイトのINC命令(1を加算)およびDEC命令(1を減算)を意味する。64ビット・モードでは,REXプレフィックスのコードに相当するこれらの命令はオペコードが変更されている。例えば「INC EAX」はFFH C0Hである。

32ビットCPUとしても振る舞う

 x64アーキテクチャが備えるもう1つのモードであるレガシー・モードは,既存のAthlon XPやPentium 4などと同様に,(1)リアル・モード,(2)仮想8086モード,(3)プロテクト・モード——の3つのモードが,サブモードとして存在する。

 レガシー・モードでは,RAXなどの64ビットに拡張されたレジスタや,R8~R15とXMM8~XMM15の追加されたレジスタは使えない。振る舞いは,32ビットCPUと全く同じになる。この点で,80386で16ビットから32ビットに拡張されたときには,リアル・モード(16ビットのモード)でも32ビット・レジスタが利用できたのと異なる。64ビットの機能を利用するためには,必ず64ビット・モードにする必要がある。従ってそのためには,64ビットOSが必須である。

WOW64でそのまま動作

 次に,64ビットWindows上で既存の32ビット・アプリケーションを動作させるWOW64について説明する。WOW64の動作を理解するためには,32ビットWindowsの基本的なアーキテクチャに対する知識が必要なので,まずはこれについて簡単に説明する。

 32ビットWindows上では,user32.dll,gdi32.dll,kernel32.dll,ntdll.dllといったシステムDLLがユーザー・モードで動作する。例えばgdi32.dllはGUI操作のときに呼び出され,プロセスに関する処理をしたときはkernel32.dllが呼び出される。それらのDLLは,カーネル・モードでの処理(システム・コール)に移行するときに,win32k.sysやntoskrnl.exeを呼び出す(図12[拡大表示])。

 このような仕組みは,64ビットWindowsでも,64ビット・アプリケーションに関しては全く同じである。gdi32.dllなど同名のシステム・ファイルがある。ファイル名には「32」と付けられているが,64ビット・プログラムである。

 一方,32ビット・アプリケーションを動かすために,64ビットWindowsも32ビットWindowsと同じ32ビットのシステム・ファイルを備えている。ちなみにこれらは64ビット用とは別に,%windir%\SysWOW64フォルダに格納されている。

 ただし,64ビットWindowsには,32ビットのカーネル・モード・システム・モジュール(win32k.sysやntoskrnl.exe)がない。64ビットWindowsでは,WOW64が32ビットのカーネル・モード・システム・モジュールの代わりを担っているのである(図12b)。

32ビットと64ビットの仲立ちをする

 WOW64の実体は,WOW64.dll,WOW64win.dll,WOW64cpu.dllという3つのDLLである。これら3つのDLLは,32ビット・プロセスにロードできる特殊な64ビット・モジュールになっている。これらのDLLは,以下に挙げた理由などから,ユーザー・モードで動作する。

 1つ目の理由は,64ビット・カーネルの負担を少なくするためだ。WOW64をユーザー・モードで動かすことで,モードの切り替えであるコンテキスト・スイッチが減り,システム全体のパフォーマンス向上につながる。

 もう1つの理由は,安全性を考慮したことだ。32ビット・アプリケーションがカーネル・モード相当でクラッシュしても,ユーザー・モードで動かしていればシステムに影響はない。

 次にWOW64を構成する3つのDLLの処理内容を詳しく見ていこう。

 WOW64.dllは,32ビット・モジュールからのシステム・コール(本来32ビットのntoskrnl.exeなどにされるべき呼び出し)を横取りし,64ビットのntoskrnl.exeを呼び出す。同様に,WOW64win.dllは,32ビット・モジュールからのwin32k.sysへの呼び出しを横取りし,64ビットのwin32k.sysを呼び出す。

 このとき,32ビットDLLがシステム・コールするためにスタックに積んだ引数を抽出し,64ビット用に変換した上で64ビット・システム・コールを発行する。その際,32ビットの構造体(複数のデータをひとまとまりにしたもの)があれば,64ビットの構造体に変換する。その内容は,ポインタ型データの大きさを32ビット(4バイト)から64ビット(8バイト)に変換すること,アラインメント(データの区切れ目)を調整することである。32ビット構造体のアラインメントは4バイトである。例えば,1バイト・データがあれば,その後ろにダミー・データが3バイト付けられる。これに対して64ビット構造体のアラインメントは8バイトになる。

 ちなみに,int型(符号付き整数を扱うデータ型)などのデータ型は,互換性を重視して,32ビットWindowsと大きさが同じになっている。つまり,64ビットWindowsでも,int型の大きさは32ビットである。

 一方,仮想アドレス空間については,32ビット・プロセスの4Gバイトの空間を,64ビット・プロセスの16Tバイト(のうちのユーザー領域8Tバイト)にマップし直す。それほどパフォーマンス低下を引き起こす原因にはならないはずだ。

 これらの仕組みから,先に64ビットWindowsも32ビットWindowsと同じ32ビットのシステムDLLを備えていると説明したが,全く同じではないDLLが存在することに気が付くだろう。それは,システム・コールを行うkernel32.dll,ntdll.dll,user32.dll,imm32.dll,gdi32.dll,rpcrt4.dllである。「WOW64.dllが32ビット・モジュールのシステム・コールを横取りする」と説明したが,実際には横取りしているわけではない。64ビットWindowsが備えるこれらの32ビット版DLLは,システム・コール時にWOW64cpu.dllを呼び出すよう変更されている。呼び出されたWOW64cpu.dllは,CPUの動作モードを64ビット・モードに切り替え,WOW64.dllを呼び出す処理を担っている。

 このようなアーキテクチャはIPF版と基本的に同じだ。現在のx64 Editionは,IPF版とソース・コードを共通にして開発されている。アプリケーションも,x64版とIPF版とで,ソース互換にできる。ひとたびx64向けにアプリケーションを開発すれば,それはすなわちIPF対応アプリケーションになる。

 WOW64は,ファイル・システムやレジストリのリダイレクトも行っている。上で述べたように,64ビットWindowsには,64ビットと32ビットの同じ名前のDLLが用意されている。それが格納されているのは,64ビット版が%windir%\system32,32ビット版が%windir%\SysWOW64である。紛らわしいが,「system32」に含まれているのが64ビット版である。また,アプリケーションを格納するフォルダも分かれている。64ビット・アプリケーションは%SystemDrive%\Program Files,32ビット・アプリケーションは%SystemDrive%\Program Files(x86)に,それぞれ格納される(図13[拡大表示])。

ファイルやレジストリをリダイレクト

 64ビットWindowsでは,32ビット・アプリケーションから呼び出せるDLLは32ビット版だけである。そのため,DLLのロード元を「C:\WINDOWS\system32」のようにハードコーディングしている32ビット・アプリケーションは,そのままでは64ビットWindows上でDLLをロードできず,動かなくなってしまう。

 そこで64ビットWindows(WOW64)は,32ビット・アプリケーションが,例えばC:\WINDOWS\system32にアクセスすると,C:\WINDOWS\SysWOW64にリダイレクトする(図14[拡大表示])。WOW64によるリダイレクトは,Wow64EnableWow64FsRedirection APIで制御可能だ。このAPIは,真または偽を表すbool型の引数を1つ取り,TRUE(真)を指定するとリダイレクトが有効に,FALSE(偽)を指定すると無効になる(標準でリダイレクト有効)。なお,WOW64のシステム・フォルダ名は,GetSystemWow64Directory APIによって取得できる。

 レジストリに関しても同様である。64ビットWindowsのレジストリには,32ビット・アプリケーション用のキーとして「HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node」がある。32ビット・アプリケーションが,HKEY_LOCAL_MACHINE\SOFTWARE以下にアクセスすると,それをHKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node以下にリダイレクトする。

 64ビットWindowsには64ビット版と32ビット版の2つのregedit.exeがあるので,実際にレジストリのHKEY_LOCAL_MACHINE\SOFTWARE以下を見てみると図15[拡大表示]のようになる。64ビット版regeditでは,HKEY_LOCAL_MACHINE\SOFTWAREの下にWow6432Nodeが表示される。これに対して32ビット版のregeditでは,HKEY_LOCAL_MACHINE\SOFTWAREしか表示されない。

 また表示される内容を見てみると,64ビット版regeditでは,HKEY_LOCAL_MACHINE\SOFTWAREの下に「サンプル64」,HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Nodeの下に「サンプル32」がそれぞれ表示される。それに対して32ビット版regeditでは,HKEY_LOCAL_MACHINE\SOFTWAREの下に「サンプル32」が表示されるだけである。これらのことから,32ビット版regeditが表示しているHKEY_LOCAL_MACHINE\SOFTWARE以下のレジストリは,64ビット版regeditのHKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node以下に相当していることが分かる。

出典:日経BPムック『完全詳説! Visual Studio 2005 & SQL Server 2005』 (2005年5月23日発行) より
記事は執筆時の情報に基づいており、現在では異なる場合があります。