64bit版Windowsで
32bit版アプリを動かすための仕組みを活用
Windows On ARMには、x86ネイティブコードで作られたデスクトップアプリケーションのエミュレーション実行環境がある。これはWOW(Windows On Windows)を応用したものだ。
WOWは、x64版Windowsでx86ネイティブコードの32bitデスクトップアプリケーションを実行するために作られた。簡単にいうと、x86版Windowsのエミュレーション環境である。また、過去に存在したItanium(IA-64)版Windowsでは、ここでx86ネイティブコードのエミュレーション実行をサポートしていた。
Windowsでは、さまざまなAPIはDLL(Dynamic Link Library)内のコードが実現している。システムDLLは、必要に応じて、Windows Executiveにあるカーネルやマネージャー、デバイスドライバ、Win32サブシステムなどの機能を呼び出す。この基本的な仕組みは、x86でもx64でも同じである。
Kernel32.DLL、User32.DLL、GDI32.DLL、AdvAPI32.DLLの4つのDLLは、Windowsのカーネルやデバイスドライバなどが動作するカーネルモード環境であるWindows Executive側の機能を呼び出す。他のシステムDLLからは、カーネルやデバイスドライバなどへのアクセスはこの4つのDLLを呼び出して行なう。これらのDLLは、カーネル側の機能などを利用する場合に必ず通る通過点と言っていい。WOWでは、この仕組みを元に64bit環境にあるカーネルの呼び出しを変換している。
Windowsのx64とx86では、APIの仕様や関数の呼び出し方(呼び出し規約)に違いがある。x64環境のAPIセットであるWin64では、メモリアドレスを64bitで表現するのに対して、x86のAPIセットWin32では32bitで表現する。INT型(32bit整数)などの基本データ形式は、64bit環境やWin64でもそのままになっていて、多くのAPI引数はそのまま使えるがアドレスを受け渡しする場合には、32bitと64bitという違いが生じる。
特に問題となるのは、APIなどで使われるさまざまな構造体だ。構造体とは、INT型や浮動小数点形式、ポインターなどを組みあわせて特定のAPIに必要なデータをひとまとめにしたものだ。WindowsのAPIなので、マイクロソフトにとって構造は既知のものであるが、構造体内部にポインターがあれば、これを32bitから64bitに変換する必要がある。
しかし、構造体はAPIごとに必要に応じて定義されるために多数あり、APIの呼び出しごとに、使われている構造体を判断して、変換を行う必要がある。場合によっては、構造体の中にあるポインタがさらに別の構造体を示しているといった入れ子構造もありえる。
たとえば、ファイルを扱う場合に利用するファイルハンドルは、Windowsが内部的に利用しているファイルアクセスのための構造体の1つを指し示す。この中には、ファイルの読み書きに利用する作業領域のアドレスが格納されている。Win32では、このアドレスは32bitだが、Win64では64bitアドレスとなる。API自体はメモリアドレスを引数などで取り扱わなくても、APIを実現するコードでは、32bitのアドレスを扱っていることが少なくない。
x64環境では、実際にファイルアクセスを行うのは64bit側のコードなので、64bitアドレスが使われるが、Win32側と互換性を持たせようとすると、32bit環境からアクセス可能なメモリアドレスを使わねばならないなどの条件を満たしつつ変換作業をその都度する必要がある。
だとしたら逆に32bit環境でメモリ割り当てなどをしてしまい、x86側はあくまでも32bit環境のままとしてAPIの処理の大半をさせたほうが変換の手間は最小限となる。
また、Win64ではAPIを呼び出す場合、引き渡すパラメーター(引数)のうち先頭の4つをx64の汎用レジスタに格納する。これに対して、x86では全ての引数はスタックに格納してAPI呼び出しが行なわれる。
このため64bit側で実行すべきAPIを呼び出す場合、スタックに格納されている引数を読み込んでレジスタに設定する必要がある。同様にAPIから戻ってきた場合、戻り値などをWin32の形式に合わせる作業が必要になる。一回の呼び出しに対する処理はそれほど大きなものではないものの、API呼び出しは多数あるため、全体としてみるとその負荷や処理時間は無視できないほどになる。
WOWでは、32bit版Windowsを構成するDLLなどをほぼそのまま使い、32bit環境を作り出してその中でWin32アプリケーションを動作させる。このようにすることで、APIを処理するコードであっても、カーネルやデバイスドライバを呼び出さない部分では、32bitアドレスのWin32のまま動作ができる。APIは必ずしもカーネルを呼び出すとは限らず、大部分のコードは32bitのままで動作することで、アドレスや構造体の変換などを最小限にとどめることができる。
WOWと64bit CPU
Windowsの64bit化にあたり、Itanium(IA-64)版が先に作られ、x86の64bit版となるx64があとから登場した。IA-64は、バイナリレベルではx86と互換性はないものの、インテルによりエミュレーターが開発されていた(初期のItaniumにはエミュレーションを支援するハードウェアが搭載されていた)。
このため、従来のx86コードを動かすのにx86コードを利用するWOWのような環境が必要だったわけだ。逆にx64への対応にあたっては、WOWの仕組みがそのまま残された。政治的妥協の産物なのか、あるいはマイクロソフトに先見の明があったのかは不明だが、すでに開発が終了したIA-64版Windows由来の仕組みがWindows On ARMのx86デスクトップアプリケーションの実行エミュレーションに利用できたわけだ。
Windows On ARMでは、WOW64と同じく、x86ネイティブコードのアプリケーションとWin32APIを提供するシステムDLLはそのまま使われ、これをx86コードのエミュレーターが実行する。このエミュレーターは、「Dynamic Binary Translator」と呼ぶ。
これは、x86がプログラムを実行するように、コードを追いかけながら、コードをまとまった単位で実行時にAArch64命令に変換しながら実行する。この変換結果は、ファイルに記録され次回以降は、これを利用してコード変換を省略することができる。ただし変換結果は、実行した部分のみであり、実行されなかったブロックなどは変換されていないため、元のx86アプリケーション全体をコード変換したものとは違っている。
同様の仕組みは、.NET FrameworkのCLRにも含まれている。CLRは、仮想マシンコードCLI(Common Language Infrastructure)を実行環境のネイティブコードに変換して実行する。マイクロソフトは、QuickBASIC以来、こうしたリアルタイムのコンパイル技術を開発しつづけており、SUN Microsystemsとの訴訟になったVisual Javaでは、やはりJava仮想コードをリアルタイムにネイティブコードに変換することで本家SUNのJavaよりも高速な実行を可能にしていた。
エミュレーションでシステムDLLまで実行するのは、実行するアプリケーションがDLLを直接呼び出しており、同じx86のデータ形式、アドレス形式を利用しているからだ。通常DLLは、アプリケーションと同じメモリ空間に割り当てられて実行されるため、x86ネイティブコード形式のままのほうが都合がよい。
ただし、高速とはいえエミュレーションには時間がかかる。そこで、マイクロソフトは、x86ネイティブコードとAArch64の2つのネイティブコードを1つの実行ファイルに格納するCHPE(Compiled Hybrid PE)というファイル形式を開発した。
これは、同一のソースコードをx86、AArch64などアーキテクチャの違うプロセッサのそれぞれのネイティブコードにコンパイルしたあと、1つの実行ファイルにまとめるものだ。PEとは、「Portable Executable」の略でマイクロソフトが定義した実行ファイル形式の構造のことだ。EXEファイルやDLLファイルはすべてPE形式になっている。
Windows On ARMのWOW環境にあるx86システムDLLの一部は、CHPEとなっていて、あらかじめAArch64のコードを含んでいる。しかし、このAArch64コードは、Windows On ARMの通常のDLLとは違いWin32のやり方でデータを扱い、APIコールを行なう。64bit環境側のカーネルなどを呼び出す場合には、WOW側の機能を利用する。これによりWOW環境内でAArch64を使って直接コードを実行できるようになる。
この場合、コードはすべてAArch64に変換されているため、どの部分も変換の手間なしに実行が可能だ。おそらくNTDLL.DLL、KERNEL32.DLL、USER32.DLL、GDI.DLL、AdvAPI.DLLなどのカーネル呼び出しにつながるDLLは、CHPE形式になっていると推測される。
当初の発表では、Windows On ARMを搭載したPCは、年内に登場とのことだったが、現在まだ発表されていないことを考えると、アリバイ的に年内に出荷するか、来年にずれ込む可能性もある。また、Windows On ARMは、RS3ことFall Creators Updateで対応とされてきたが、開発が遅れたあるいは検証に時間がかかるなどで、来年3月のRS4に合わせて発表という可能性もあるだろう。
さて、次回は、Windows On ARMで使われるARMv8アーキテクチャや省電力の仕組みなどを見ながら、Windowsの実装を考えることにする。
この連載の記事
-
第466回
PC
PowerToysの最近の新機能には、複数アプリを指定位置に起動する「ワークスペース」や新規作成のカスタマイズがある -
第465回
PC
WindowsのPowerShellからBluetoothデバイスを調べる -
第464回
PC
Windows 10のサポート切れまで1年を切った さてWindows 10マシンをどうする? -
第463回
PC
Windows Terminal Preview版でSixelグラフィックスを実際に表示させてみる -
第462回
PC
Windows Terminal Preview版でSixelグラフィックスを扱う -
第461回
PC
Copilot+ PCを買ってみたが、「今焦って買う必要はない」のかもしれない -
第460回
PC
Windowsでsftpを使う -
第459回
PC
WSL 2.4.4ではtar形式でのディストリビューションが配布でき、企業での利用が容易になってきた -
第458回
PC
Windows上でhostsファイルを活用する -
第457回
PC
IPv6アドレスは先頭を見ればどんな種類かわかる -
第456回
PC
あらためてIPv6基本のキ - この連載の一覧へ