---
title: 第10章 Linux® バイナリ互換機能
part: パートII. 日々の生活
prev: books/handbook/printing
next: books/handbook/partiii
showBookMenu: true
weight: 13
params:
  path: "/books/handbook/linuxemu/"
---

[[linuxemu]]
= Linux(R) バイナリ互換機能
:doctype: book
:toc: macro
:toclevels: 1
:icons: font
:sectnums:
:sectnumoffset: 10
:partnums:
:source-highlighter: rouge
:experimental:
:images-path: books/handbook/linuxemu/

ifdef::env-beastie[]
ifdef::backend-html5[]
:imagesdir: ../../../../images/{images-path}
endif::[]
ifndef::book[]
include::shared/authors.adoc[]
include::shared/mirrors.adoc[]
include::shared/releases.adoc[]
include::shared/attributes/attributes-{{% lang %}}.adoc[]
include::shared/{{% lang %}}/teams.adoc[]
include::shared/{{% lang %}}/mailing-lists.adoc[]
include::shared/{{% lang %}}/urls.adoc[]
toc::[]
endif::[]
ifdef::backend-pdf,backend-epub3[]
include::../../../../../shared/asciidoctor.adoc[]
endif::[]
endif::[]

ifndef::env-beastie[]
toc::[]
include::../../../../../shared/asciidoctor.adoc[]
endif::[]

[[linuxemu-synopsis]]
== この章では

FreeBSD は、Linux(R) とのバイナリ互換機能を提供しています。 このバイナリ互換機能を使うことで、ユーザは、ほとんどの Linux(R) バイナリを変更することなく、FreeBSD システム上にインストールして実行できるようになります。 ある状況においては Linux(R) バイナリを Linux(R) で動かすよりも FreeBSD で動かすほうが良いパフォーマンスが出るという報告もあります。

しかしながら、いくつかの Linux(R) に特有なオペレーティングシステムの機能は FreeBSD ではサポートされていません。たとえば、 仮想 8086 モードを有効にするような i386(TM) 特有の呼び出しを過度に使う Linux(R) バイナリは FreeBSD では動きません。

[NOTE]
====
64 ビットの Linux(R) バイナリ互換機能は、 FreeBSD 10.3 で追加されました。
====

この章を読むと、以下のことがわかります。

* FreeBSD システムで Linux(R) バイナリ互換機能を有効にする方法。
* Linux(R) 共有ライブラリを追加する方法。
* Linux(R) アプリケーションを FreeBSD システムにインストールする方法
* FreeBSD における Linux(R) 互換機能の実装の詳細。

この章を読む前に、以下のことを理解しておく必要があります。

* crossref:ports[ports,サードパーティ製ソフトウェア] のインストール方法

[[linuxemu-lbc-install]]
== Linux(R) バイナリ互換機能の設定

Linux(R) ライブラリは、デフォルトでは FreeBSD にインストールされません。 また、Linux(R) バイナリ互換機能も、デフォルトでは有効ではありません。 Linux(R) ライブラリは、手動もしくは FreeBSD Ports Collection を使ってインストールできます。

port を構築する前に、 `linux` カーネルモジュールを読み込んでください。 このモジュールを読み込んでいないと、構築に失敗してしまいます。

[source,shell]
....
# kldload linux
....

64 ビットの互換機能を使うには、以下を実行してください。

[source,shell]
....
# kldload linux64
....

以下のようにしてモジュールが読み込まれていることを確認してください。

[source,shell]
....
% kldstat
      Id Refs Address    Size     Name
      1    2 0xc0100000 16bdb8   kernel
      7    1 0xc24db000 d000     linux.ko
....

Linux(R) ライブラリおよびバイナリの基本セットを FreeBSD システムにインストールする最も簡単な方法は、 package:emulators/linux_base-c7[] package または port を使う方法です。port をインストールするには、 以下のコマンドを実行してください。

[source,shell]
....
# pkg install emulators/linux_base-c7
....

起動時から Linux(R) 互換機能を有効にする場合には、 [.filename]#/etc/rc.conf# に以下の行を追加してください。

[.programlisting]
....
linux_enable="YES"
....

64 ビットのコンピュータでは、 [.filename]#/etc/rc.d/abi# により 64 ビット互換のためのモジュールは自動的に読み込まれます。

Linux(R) バイナリ互換機能のレイヤには、(64 ビット x86 ホストにおける) 32 および 64 ビット Linux バイナリのサポートが追加されたため、 エミュレーション機能をカスタムカーネルに静的にリンクする必要はありません。

[[linuxemu-libs-manually]]
=== 手動によるライブラリの追加のインストール

Linux(R) バイナリ互換機能を設定した後に、Linux(R) アプリケーションが必要な共有ライブラリが存在しないというエラーを出した場合には、 Linux(R) バイナリがどの共有ライブラリを必要としているかを確認して、 手動でインストールしてください。

Linux(R) システムで、`ldd` を使うことにより、 アプリケーションが必要とする共有ライブラリを調べることができます。 たとえば、`linuxdoom` が必要とする共有ライブラリを調べるには、 Doom がインストールされている Linux(R) システム上で、以下のコマンドを実行してください。

[source,shell]
....
% ldd linuxxdoom
libXt.so.3 (DLL Jump 3.1) => /usr/X11/lib/libXt.so.3.1.0
libX11.so.3 (DLL Jump 3.1) => /usr/X11/lib/libX11.so.3.1.0
libc.so.4 (DLL Jump 4.5pl26) => /lib/libc.so.4.6.29
....

Linux(R) システムでの出力の最後のカラムに表示されているすべてのファイルを FreeBSD システムの [.filename]#/compat/linux# の下にコピーしてください。コピーしたら、 最初のカラムに示されるファイル名でコピーしたファイルに対してシンボリックリンクを張ってください。 この例では、FreeBSD システムで以下のようになります。

[source,shell]
....
/compat/linux/usr/X11/lib/libXt.so.3.1.0
/compat/linux/usr/X11/lib/libXt.so.3 -> libXt.so.3.1.0
/compat/linux/usr/X11/lib/libX11.so.3.1.0
/compat/linux/usr/X11/lib/libX11.so.3 -> libX11.so.3.1.0
/compat/linux/lib/libc.so.4.6.29
/compat/linux/lib/libc.so.4 -> libc.so.4.6.29
....

`ldd` の出力の最初のカラムに表示されているメジャーバージョンが同じ Linux(R) 共有ライブラリが既にインストールされている場合は、 最後のコラムにある名前のファイルを新たにコピーする必要はありません。 既にあるライブラリで動作するはずです。 ただ、新しいバージョンの共有ライブラリがある場合には、 コピーすることをお奨めします。 新しいライブラリにシンボリックリンクを変更したら、 古いライブラリは削除してかまいません。

たとえば、以下のライブラリがすでに FreeBSD システムに存在するとします。

[source,shell]
....
/compat/linux/lib/libc.so.4.6.27
/compat/linux/lib/libc.so.4 -> libc.so.4.6.27
....

そして、`ldd` の出力が以下のように、 バイナリが新しいバージョンを必要とする場合を考えます。

[source,shell]
....
libc.so.4 (DLL Jump 4.5pl26) -> libc.so.4.6.29
....

存在しているライブラリの最後の番号が 1 つか 2 つ古いだけなので、 わずかに古いライブラリでもプログラムは動作するはずです。 しかしながら、[.filename]#libc.so# を新しいバージョンに置き換えるのが安全です。

[source,shell]
....
/compat/linux/lib/libc.so.4.6.29
/compat/linux/lib/libc.so.4 -> libc.so.4.6.29
....

通常は、Linux(R) のバイナリが必要とする共有ライブラリを探す必要があるのは、 FreeBSD のシステムに Linux(R) のプログラムをインストールする最初の数回だけです。 それが過ぎれば、十分な Linux(R) の共有ライブラリがシステムに存在するので、 新しくインストールした Linux(R) のバイナリも追加の作業をせずに動作させることができるようになります。

=== Linux(R) の ELF バイナリのインストール

ELF のバイナリを使うためには、 追加の作業が必要です。 マークのない (unbranded) ELF バイナリを実行しようとすると、 以下のようなエラーメッセージが表示されてしまうことでしょう。

[source,shell]
....
% ./my-linux-elf-binary
ELF binary type not known
Abort
....

FreeBSD のカーネルが FreeBSD の ELF バイナリと Linux(R) のバイナリとを見分けられるようにするために、man:brandelf[1] を以下のようにして使ってください。

[source,shell]
....
% brandelf -t Linux my-linux-elf-binary
....

GNU のツール群が ELF バイナリに自動的に適切なマークを付加するようになったので、 この作業は通常必要ありません。

=== Linux(R) RPM ベースのアプリケーションのインストール

Linux(R) RPM ベースのアプリケーションをインストールするには、 最初に package:archivers/rpm4[] package または port をインストールしてください。 インストールすると、このコマンドを `root` 権限で使うことで、 [.filename]#.rpm# をインストールできます。

[source,shell]
....
# cd /compat/linux
# rpm2cpio < /path/to/linux.archive.rpm | cpio -id
....

必要に応じて、インストールした ELF バイナリに `brandelf` を実行してください。 綺麗にアンインストールできないかもしれませんので注意してください。

=== ホストネームリゾルバの設定

DNS がうまく動作しなかったり、 以下のようなエラーメッセージが表示される場合は、 [.filename]#/compat/linux/etc/host.conf# ファイルを以下のように設定する必要があります。

[source,shell]
....
resolv+: "bind" is an invalid keyword resolv+:
"hosts" is an invalid keyword
....

ファイルの内容を以下のように設定してください。

[.programlisting]
....
order hosts, bind
multi on
....

この設定では [.filename]#/etc/hosts# を最初に検索し、 次に DNS を検索するように指定します。 [.filename]#/compat/linux/etc/host.conf# が存在しない場合には、 Linux(R) アプリケーションは [.filename]#/etc/host.conf# を使用しようとし、 FreeBSD の文法とは互換性がないと警告を出力します。 [.filename]#/etc/resolv.conf# を利用してネームサーバの設定をしていない場合には、 `bind` を削除してください。

[[linuxemu-advanced]]
== 高度なトピックス

この節では、Linux(R) バイナリ互換機能がどのような仕組みで動作をしているかを説明します。 以下の文章は {freebsd-chat} に投稿された Terry Lambert (mailto:tlambert@primenet.com[tlambert@primenet.com]) 氏のメール (Message ID: `<199906020108.SAA07001@usr09.primenet.com>`) をもとにしています。

FreeBSD は、"実行クラスローダ (execution class loader) " と呼ばれる抽象的な機構を持っています。これは man:execve[2] システムコールへの楔という形で実装されています。

歴史的には、UNIX(R) のローダはマジックナンバー (一般的にはファイルの先頭の 4 ないし 8 バイトの部分) の検査を行ない、システムで実行できるバイナリかどうかを検査し、 もしそうならバイナリローダを呼び出すというようになっていました。

もし、そのシステム用のバイナリでない場合には、 man:execve[2] システムコールの呼び出しは失敗の戻り値を返し、 シェルがシェルコマンドとして実行しようと試みていたわけです。 この仮定は"現在利用しているシェルがどのようなものであっても"デフォルトでした。

後に man:sh[1] に変更が加えられ、先頭の 2 バイトを検査した結果 `:\n` であれば代わりに man:csh[1] を呼び出す、 というようになりました。

FreeBSD は、単一のローダではなく、ローダの一覧を走査します。 動作しているシェルインタプリタもしくはシェルスクリプトとして、 該当するものが存在しなければ、`#!` ローダが用いられます。

Linux(R) ABI をサポートするため、FreeBSD は ELF バイナリを示すマジックナンバを確認します。 ELF ローダは、特殊な__マーク (brand)__ があるかどうか探します。 このマークとは、ELF イメージのコメントセクションのことです。 SVR4/Solaris(TM) の ELF バイナリには、このセクションは存在しません。

Linux(R) バイナリを実行するためには、 man:brandelf[1] を使って `Linux` のマークが__付けられて__いなければなりません。

[source,shell]
....
# brandelf -t Linux file
....

ELF ローダが `Linux` マークを確認すると、 ローダは `proc` 構造体内の ある一つのポインタを置き換えます。システムコールは全て、 このポインタを通してインデックスされます。 さらに、そのプロセスには Linux(R) カーネルモジュールに必要なシグナルトランポリンコード (訳注: シグナルの伝播を実現するコード) 用の特殊なトラップベクタの設定や、 他の (細かな) 調整のための設定が行なわれます。

Linux(R) システムコールベクタは、 さまざまなデータに加えて `sysent[]` エントリーのリストを含んでおり、 それらのアドレスはカーネルモジュール内にあります。

Linux(R) バイナリがシステムコールを発行する際、トラップコードは `proc` 構造体を用いてシステムコール関数ポインタを 解釈します。そして FreeBSD ではなく Linux(R) 用のシステムコールエントリポイントを得るわけです。

Linux(R) モードは状況に応じて__ファイルシステム本来のルートマウントポイントを置き換えて__ファイルの参照を行ないます。 これは、`union` を指定してマウントされたファイルシステムが行なっていることと同じです。 ファイルを検索する際にはまず [.filename]#/compat/linux/original-path# を調べます。見つけられなかったときには、 [.filename]#/original-path# を調べます。 こうすることで、他のバイナリを要求するバイナリの実行を可能にしています。 たとえば、Linux(R) 用ツールチェインは Linux(R) ABI サポート環境下で完全に動作します。 またこれは、もし対応する Linux(R) バイナリが存在しない場合に Linux(R) バイナリが FreeBSD バイナリをロードしたり、 実行したりすることが可能であること、 その Linux(R) バイナリに自分自身が Linux(R) 上で実行されていないことを 気付かせないようにする目的で、man:uname[1] コマンドを [.filename]#/compat/linux# ディレクトリに置くことができる、 ということを意味します。

要するに、Linux(R) カーネルが FreeBSD カーネルの内部に存在しているわけです。 カーネルによって提供されるサービス全ての実装の基礎となるさまざまな関数は FreeBSD システムコールテーブルエントリと Linux(R) システムコールテーブルエントリの両方で共通に利用されています。 これらにはファイルシステム処理、仮想メモリ処理、シグナル伝送、 System V IPC が含まれますが、 FreeBSD バイナリは FreeBSD __グルー__ (訳注: glue; 二者の間を仲介するという意味) 関数群、 そして Linux(R) バイナリは Linux(R) __グルー__関数群を用いる、 という点だけが異なります。 FreeBSD の__グルー__関数群は、 カーネルの中に静的にリンクされ、 Linux(R) の__グルー__関数群は静的にリンクすることも、 カーネルモジュールを介して利用することもできるようになっています。

技術的には、これはエミュレーションではなく、 ABI の実装です。 よく "Linux(R) エミュレーション"と呼ばれるのは、 この機能が初めて実装された頃、 この機能を表現する言葉がなかったためです。 コードをコンパイルしてはいないので、 FreeBSD 上で Linux(R) バイナリを実行するという表現は、 厳密に考えると適切ではありません。
