この節では、Linux®
バイナリ互換機能がどのような仕組みで動作をしているかを説明します。
以下の文章は FreeBSD chat メーリングリスト に投稿された
Terry Lambert (<tlambert@primenet.com>
) 氏のメール
(Message ID: <199906020108.SAA07001@usr09.primenet.com>
)
をもとにしています。
FreeBSD は、「実行クラスローダ (execution class loader) 」 と呼ばれる抽象的な機構を持っています。これは execve(2) システムコールへの楔という形で実装されています。
歴史的には、UNIX® のローダはマジックナンバー (一般的にはファイルの先頭の 4 ないし 8 バイトの部分) の検査を行ない、システムで実行できるバイナリかどうかを検査し、 もしそうならバイナリローダを呼び出すというようになっていました。
もし、そのシステム用のバイナリでない場合には、 execve(2) システムコールの呼び出しは失敗の戻り値を返し、 シェルがシェルコマンドとして実行しようと試みていたわけです。 この仮定は「現在利用しているシェルがどのようなものであっても」デフォルトでした。
後に sh(1) に変更が加えられ、先頭の 2 バイトを検査した結果
:\n
であれば代わりに csh(1) を呼び出す、
というようになりました。
FreeBSD は、単一のローダではなく、ローダの一覧を走査します。
動作しているシェルインタプリタもしくはシェルスクリプトとして、
該当するものが存在しなければ、#!
ローダが用いられます。
Linux® ABI をサポートするため、FreeBSD は ELF バイナリを示すマジックナンバを確認します。 ELF ローダは、特殊なマーク (brand) があるかどうか探します。 このマークとは、ELF イメージのコメントセクションのことです。 SVR4/Solaris™ の ELF バイナリには、このセクションは存在しません。
Linux® バイナリを実行するためには、
brandelf(1) を使って Linux
のマークが付けられていなければなりません。
#
brandelf -t Linux file
ELF ローダが Linux
マークを確認すると、
ローダは proc
構造体内の
ある一つのポインタを置き換えます。システムコールは全て、
このポインタを通してインデックスされます。
さらに、そのプロセスには Linux®
カーネルモジュールに必要なシグナルトランポリンコード (訳注:
シグナルの伝播を実現するコード) 用の特殊なトラップベクタの設定や、
他の (細かな) 調整のための設定が行なわれます。
Linux® システムコールベクタは、
さまざまなデータに加えて sysent[]
エントリーのリストを含んでおり、
それらのアドレスはカーネルモジュール内にあります。
Linux® バイナリがシステムコールを発行する際、トラップコードは
proc
構造体を用いてシステムコール関数ポインタを
解釈します。そして FreeBSD ではなく
Linux® 用のシステムコールエントリポイントを得るわけです。
Linux®
モードは状況に応じてファイルシステム本来のルートマウントポイントを置き換えてファイルの参照を行ないます。
これは、union
を指定してマウントされたファイルシステムが行なっていることと同じです。
ファイルを検索する際にはまず
/compat/linux/original-path
を調べます。見つけられなかったときには、
/
を調べます。
こうすることで、他のバイナリを要求するバイナリの実行を可能にしています。
たとえば、Linux® 用ツールチェインは
Linux® ABI サポート環境下で完全に動作します。
またこれは、もし対応する Linux® バイナリが存在しない場合に
Linux® バイナリが FreeBSD バイナリをロードしたり、
実行したりすることが可能であること、
その Linux® バイナリに自分自身が Linux® 上で実行されていないことを
気付かせないようにする目的で、uname(1) コマンドを
original-path
/compat/linux
ディレクトリに置くことができる、
ということを意味します。
要するに、Linux® カーネルが FreeBSD カーネルの内部に存在しているわけです。 カーネルによって提供されるサービス全ての実装の基礎となるさまざまな関数は FreeBSD システムコールテーブルエントリと Linux® システムコールテーブルエントリの両方で共通に利用されています。 これらにはファイルシステム処理、仮想メモリ処理、シグナル伝送、 System V IPC が含まれますが、 FreeBSD バイナリは FreeBSD グルー (訳注: glue; 二者の間を仲介するという意味) 関数群、 そして Linux® バイナリは Linux® グルー関数群を用いる、 という点だけが異なります。 FreeBSD のグルー関数群は、 カーネルの中に静的にリンクされ、 Linux® のグルー関数群は静的にリンクすることも、 カーネルモジュールを介して利用することもできるようになっています。
技術的には、これはエミュレーションではなく、 ABI の実装です。 よく 「Linux® エミュレーション」と呼ばれるのは、 この機能が初めて実装された頃、 この機能を表現する言葉がなかったためです。 コードをコンパイルしてはいないので、 FreeBSD 上で Linux® バイナリを実行するという表現は、 厳密に考えると適切ではありません。
本文書、および他の文書は https://download.freebsd.org/ftp/doc/ からダウンロードできます。
FreeBSD に関する質問がある場合には、
ドキュメント を読んだ上で
<questions@FreeBSD.org> まで (英語で) 連絡してください。
本文書に関する質問については、
<doc@FreeBSD.org> まで電子メールを (英語で) 送ってください。