2013年5月30日木曜日

Wine 64bit 版は mac で動かないのか

Wine 1.5 系の開発が進んでいます。
僕はそんなに wine 詳しくないんですけど、そういえば 64bit 版って mac 向けにビルドできるのかな?と思い立ちやってみました。

結論からいうと結構あちこち直す必要があります。
大変です。カジュアルにはとてもいきません。

まずもって Xcode に付属の gcc-4.2 では wine のビルドがとおりません。
どうも可変長引数のスタックレイアウトが Win64 と互換性がないみたいで、これをサポートしてくれる gcc-4.4.4 が必要です。 mac ports で入れる分には簡単でいいですが。

そのあとはインラインasmでやたらエラーが出るのでこれを直して回ります。
movq で GP レジスタから XMM レジスタへロードするところが軒並み通らないので、メモリからのロードに変更しました。
GP レジスタへのロードはそのまま残さねばなりません。ABI の都合上、必要そうだからです。
インラインアセンブリはどこでエラーになったのかパッと見解り難いのが難ですよねぇ。
temp が残っててくれればいいのですけども。

あとは signal ハンドラのレジスタロードです。
mac 版は 32bit レジスタ用の実装しかないので、 64bit レジスタを取るように修正して回ります。

これで大体通るのですが、大きな悩みどころが残ってしまいました。
スレッドエントリの初期化で fs.base を退避/取得するところがあります。
BSD/Linux は syscall やコールゲートを使ってユーザーランドから fs.base にアクセスできるのですが、mac にはこの syscall がありません。
fs.base には rdmsr/wrmsr 命令を使ってアクセスする必要がありますが、これは特権命令なので特権モードである必要があります。

まぁ、無理に特権レジスタにアクセスすることもないか……。
ネイティブスレッドの TLS にでも入れときゃいいじゃん、ということでそういう風に書き換えたのですが、 Wow 側が fs.base に特定の値を期待してたらまずいことになります。
Win32 の頃はカーネルが FS にスレッドコンテキストのディスクリプタを入れていることも期待しており、これなら i386_set_ldt API を使って mac でも実装が可能です。
でもまぁ、プロセッサが命令レベルで特権命令だっていってるんだから、 Wow でもユーザープログラムが勝手に何かを期待することもないだろう……と祈ることにします。

(ちなみに、ユーザーランドから fs.base をリードできる新しい命令があるのですが、まだ Sandybridge では実装されてないようです。それにしても AVX はあるけど積和演算(FMA3)は実装されてないとか、色々新しい命令に取り残されている気がする。案外使えないな Sandybridge)

というわけで色々禍根を残したのですが、問題はそれだけに留まらない。
危なそうな警告を眺めていると int とポインタのキャストが結構たくさんある。
Wine の各種 driver (デバイスドライバじゃなくてプラットフォームごとのモジュールを driver と呼んでるみたい)は、例えばクリップボードの列挙を行うときに 32bit 版であれば、 Mac のネイティブ API が返すポインタを識別子としてそのまま int へキャストして返しています。

Win64 API でもそうした列挙に使う識別子はぜんぶ int のまま。一方で Mac のプロセス(Mac に限らず大抵の OS はそうだと思うが)プロセスイメージの仮想アドレスマッピングを 32bit 以上のアドレスで使います。
 仮想アドレスの割当からコントロールして 32bit に収まるアドレス空間を使うことも不可能ではないでしょうが、 OS レベルのメモリ割当を細かく使うと非常に高くつくし手間がかかる。
かといってキャストでは死んでしまいます。
下位ビット払って上位は固定……なんてのじゃダメかな。 Mac の API がどんなアドレス返すのか期待するのはかなり無理があるかな。

なのであちこちに map を作って、自分で払い出した識別子とアドレスを変換するようにしなければならない……ですね。
ああ、面倒くさい。
仕事ならやるよやりますよ。
でもこんな時間に疲れて帰って来て、寝る前にやることとしてはちょっと気が重い。

というわけで時間あるときにまたリトライしようと思います。

0 件のコメント:

コメントを投稿