2008年12月13日土曜日

LHa for Native Client (本編)

【警告】タイトルだけ見るとNative ClientでLHaが普通に動くように見えますが、実際には実用には堪えませんので、誤解のないようお願い致します。

(このエントリーは「LHa for Native Client (準備編)」の続きです。)

Native Clientの発表から、もうかなり経ちますので話題の旬はとっくに過ぎてしまったのですが、今回は実際にLHa for Native Clientを実行するところまで、何とか話を進めてみたいと思います。

ただし、LHa for Native Clientは、現時点でNative Clientがサポートしている3種類のOSの内、Mac OS XとLinuxで実行できることは確認しているのですが、Windowsでは今のところ実行に成功していません。

その理由を説明するには、そもそもNative Clientで何ができて、何ができないのか?を先に説明する必要があります。
当初はWindows版でも普通に動くだろうと思っていたので、これは誤算でした。

もう一つの誤算は、Native Clientが三日で飽きられてしまったためにx86ネイティブコードのsandbox以外の話を日本語で説明してくれるエントリに丸投げすることが出来なかったことです。
そのため、エントリがやたらと長くなってしまいましたが、どうか最後までおつきあいください。m(__)m

インブラウザモードとスタンダローンモード

Native Client上で動作する実行コード、これをGoogleはNatie Client moduleと呼んでいますので、以下では単にmoduleと呼ぶことにしますが、moduleには2種類の実行方法があります。

一つは、WebブラウザのNative Clientプラグイン上で実行する方法で、もう一つは、sel_ldrというローダを使うことで、単一のアプリケーションとして実行する方法です。

Googleは、この2種類の実行方法については、今のところ特に用語を命名してはいないようですので、ここでは仮にインブラウザモードとスタンダローンモードと呼ぶことにします。

moduleがスタンダローンモードとインブラウザモードの両方で実行可能な場合、moduleの実体である .nexeファイルは同じファイルで構いません。
基本的には同じ .nexeファイルが、Mac OS X、Linux、WindowsのどのOSでも動作します。

moduleの実行にはgccは不要

「GCCベースのコンパイラを含んだブラウザ用プラグインの形で提供され」と書いてある紹介記事を読んだために誤解している方がいらっしゃるようなので繰り返しますが、Native Clientは同一の.nexeファイルを複数のOSで実行可能なので、実行時にコンパイルは必要ありません。
Native Clientのパッケージに含まれているgccは、moduleのビルドに使われるだけです。

尚、moduleの実行環境及び開発環境そのもののビルドには、それぞれのOS向けの開発環境が別途必要になりますが、各OS向けのNative Clientパッケージはビルド済みの実行環境及び開発環境を含んでいますので、moduleをビルドして実行するだけであれば、実行環境や開発環境のビルドは不要になっています。

それから、Native Clientの実行条件として、Pythonが挙げられていますが、PythonはSconsというNative Clientの付属ツールや、スタンダローンモードのサンプルでsel_ldrを起動する際の補助に必要とされているだけで、Native Client moduleのビルドや実行に必須という訳ではないようです。

Native Client moduleの正体

正体という単語に期待した人には申し訳ないのですが、moduleの正体は、単に32-bit LSBなELF形式の実行ファイルです。

sandboxの実現のためにmoduleのビルドの際に実行コードに手を加えてはいますが、仕組み上、ELFのABIに違反するようなことはしていないはず(ここは自信なし、特にret)なので、moduleの実行をgdbで追いかけることもできるそうです。

しかし、Mac OS XのアプリケーションはMach-OというELFとは異なる形式で、付属のgdbもMach-O形式にしか対応していませんので、仮想OS上でLinuxを起動して、Linux上のgdbを使うのが無難だと思います。

それから、x86ネイティブコードのsandboxについては、id:amachangさんomoさんid:yaneuraoさんの各エントリに詳細が書いてありますので、そちらを参照することを強くオススメします。

Native Clientにできること

Native Clientは、newlibが提供する標準Cライブラリ、pthread、libSDLを使った音声と映像の出力機能の一部、NPAPIによるWebブラウザホストとの通信、IMCによるNative Client module同士の通信等が可能です。

スタンダローンモードではホストになるWebブラウザが無いためにNPAPIを使えませんが、実行時にAPIを呼び出した時点でエラーが返るだけなので、場合分けで処理を切り替えることも可能になっています。

スタンダローンモードで複数のmoduleを同時に実行してIMCで通信できるかどうかはちょっと分からないのですが、(私には難しそうですが)いつか試してみたいと思います。

映像出力と音声出力の仕様については、映像はRGBA各16ビットのフレームバッファになっています。フルスクリーンモードはありません。
「フレームバッファのハンドルはやるから好きにしろ、後は知らん」という感じですね。

音声は、44.1KHzまたは48KHzでステレオ、16ビットのリニアPCMを出力可能ですが、ヘッダのコメントによるとまだ多重再生はできないとのことなので、音声のMIXは自前で行う必要があります。

Native Clientにできないこと


Native Clientはまだバージョンが0.1ということもあって、使えるライブラリがほとんどありません。

GUIコンポーネントの表示どころか、基本的な描画ライブラリもなく、フォント、文字列エンコーディングの変換、画像、音声、動画のデコーダ等は全て自前で何とかする必要があります。

moduleは先に説明したようにELF形式なので、リンクできるライブラリもELF形式に限定され、Native Client向けにビルドしたライブラリである必要があります。
動的リンクはできないので、静的リンクのみです。

ですから、もしNative Clientで、例えばMac OS XのOpenGLライブラリを使用したいのであれば、実行環境をビルドする時にリンクして、moduleから利用するためのAPIを追加しなければいけません。しかし、それは実行環境の独自ビルドになるので、実行環境も一緒に配布しない限り、他の人のNative Client環境では実行できません。
結局、OpenGLやOpenCLについては、Googleが対応しない限りどうにもならないという事です。

Googleの今後の予定については何ともいえませんが、OpenGLやOpenCLを使えるようにするとセキュリティ上の安全性や安定性がOS側のドライバ頼みになってしまうので、見込みが薄いんじゃないかなーと個人的には思っています。あくまで個人的意見ですのであしからず。

ファイルI/Oの制限

ファイルI/Oについては、GoogleのTechnical Paperを読むと、ファイルI/OとしてPOSIX標準ファイルI/Oを利用可能だが、インブラウザモードの場合は、Web上のコンテンツの読み込みに用いられ、ファイル出力はできず、statはファイルサイズしか取れないと書いてあります。
これはHTTPではディレクトリ上のファイルの一覧を普通は取れないことを考えれば当然の仕様だと思います。

スタンダローンモードでもインブラウザモードの上記の制限を受けるために、クライアントPCのローカルファイルへのアクセスは基本的にできません。
(Native Clientがローカルファイルへのアクセスが自由に可能と説明している文章もあるようですが、それは何かの誤解だと思います。)

しかし、LHaはファイルの圧縮と圧縮を実行するツールなので、ローカルファイルにアクセスできないと意味がありません。

そこで、今回は、Native Clientに一つだけ用意されている抜け道を利用することにしました。スタンダローンモードのデバッグモードです。

デバッグモード

デバッグモードはsel_ldrに-dというオプションをつけて実行することで有効になるモードですが、このモードを使うとPOSIX標準ファイルI/Oでローカルファイルにアクセスすることが可能になります。

デバッグモードでもディレクトリエントリに対する操作はほとんど出来ませんが、ログ出力のために新規にファイルを作成したりバイナリファイルの読み書きを行う程度であれば何とかなります。

しかし、Windows版のNative Clientだけ、何故かsel_ldrでデバッグモードを有効にできません。
各OSで、スタンダローンモードでearthサンプルを実行してみた方は気づいたと思いますが、Windowsの場合だけフレーム数の表示がないことから、標準出力もできないようです。
これが、最初にWindowsでは動作しないと書いた理由です。

LHaの移植

今回のLHaの移植は、デバッグモードを利用しつつ、ディレクトリエントリが絡んだ部分を誤魔化す事で実現しています。

具体的には、Mac OS X上で普通に./configureを実行してconfig.hを作成した後で、nacl-gccでコンパイルした時にどのヘッダが足りないかを確認しながら、config.hの中身を適宜修正し、nacl-gccのリンカでエラーが出る関数をdummy.cというファイルででっちあげるという方法を取りました。

あくまで誤魔化しただけなので、ディレクトリ構造を持ったLZHファイルの作成や展開では問題が多々ありますし、実行権、シンボリックリンク、タイムスタンプ等も無視されます。最初に警告として実用には堪えないと書いたのはそのためです。

Makefileの流用も検討はしたのですが、結局、nacl_build.shというシェルスクリプトを用意しました。
結果的に出来上がったの以下のパッチとバイナリ

lha4nacl.patch
lha.nexe

lha4nacl.tar.gzというファイルにしておきました。

ビルド方法としては、Mac OS Xの場合、LHa for UNIXのソースを展開してから、上記のファイルも展開し、

$ patch -p1 <lha4nacl.patch
(nacl_build.sh 内のNACLINSTALLPATHを適宜修正)
$ ./nacl_build.sh

と実行すれば、src/lha.nexeが作成されるはずです。

lha4nacl.patchを読めば分かりますが、元のソースには一切手を加えていません。移植性を考慮したコンソールアプリのソースなら、機能制限に目をつぶれば元ソースに手を加えなくてもそこそこ動いてしまうのがNaClの面白いところだと思います。

以上、駆け足で説明しましたが、抜けているところは適宜修正していきたいと思います。
もし、何かありましたら、メールまたはKKI@Twitter等にご連絡ください。
おつきあいありがとうございました。m(__)m

【蛇足】"Native"な"Client module"

Native Clientの登場直後から、Native ClientはGoogleのOSになるのではないか?という意見を見かけますが、私はそうは思いませんでした。

というのは、GoogleがNative ClientのバイナリをNative Client Applicationではなく、Native Client moduleと一貫して呼称しているからです。

Native Clientのパッケージの階層は、nacl/google_client/nativeclient となっていますが、Native Client moduleは、"Native Client"の"module"ではなく、Google Clientの"Native"な"Client module"なのではないでしょうか?

つまり、Native Client moduleはあくまで、NativeではないClient moduleと共にGoogle Clientを構成するmoduleの一つであり、最終的には単体のアプリケーションとして動作させる利用方法は減っていくのではないか?ということです。

Google Clientがどのような物かはまだ分かりませんが 将来的には、例えばGoogle DocumentのエンジンがNative Client moduleになったりするのではないかと私は考えています。

0 コメント:

コメントを投稿