---
title: 章 10. Linux® Binary 相容性
part: 部 II. 一般作業
prev: books/handbook/printing
next: books/handbook/partiii
showBookMenu: true
weight: 13
params:
  path: "/books/handbook/linuxemu/"
---

[[linuxemu]]
= Linux(R) Binary 相容性
:doctype: book
:toc: macro
:toclevels: 1
:icons: font
:sectnums:
:sectnumlevels: 6
: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(TM) Binary 的相容性，允許使用者在 FreeBSD 系統上不需要修改就可以安裝和執行大部份的 Linux(TM) Binary。 曾經有報告指出，在某些情況下，Linux(TM) Binary 在 FreeBSD 的表現比在 Linux(TM) 好。

然而，部份特定在 Linux(TM) 作業系統上的功能在 FreeBSD 並沒有支援。例如，若 Linux(TM) Binary 過度的使用 i386(TM) 特定的呼叫，如啟動虛擬 8086 模式，會無法在 FreeBSD 執行。

[NOTE]
====
FreeBSD 10.3 後支援 64 位元的 Linux(TM) Binary 相容性。
====

讀完這章，您將了解：

* 如何在 FreeBSD 系統啟用 Linux(TM) Binary 相容模式。
* 如何安裝其他的 Linux(TM) 共用程式庫。
* 如何在 FreeBSD 系統安裝 Linux(TM) 應用程式。
* 在 FreeBSD 中 Linux(TM) 相容性的實作細節。

在開始閱讀這章之前，您需要：

* 知道如何安裝 crossref:ports[ports,其他的第三方軟體]。

[[linuxemu-lbc-install]]
== 設定 Linux(TM) Binary 相容性

Linux(TM) 程式庫預設並不會安裝，且並不會開啟 Linux(TM) Binary 相容性。 Linux(TM) 程式庫可以手動安裝或是從 FreeBSD Port 套件集安裝。

在嘗試編譯 Port 前，要載入 Linux(TM) 核心模組，否則編譯會失敗：

[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
....

在 FreeBSD 安裝基本的 Linux(TM) 程式庫和 Binary 最簡單的方式是安裝 package:emulators/linux_base-c6[] 套件或是 Port 。要安裝 Port：

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

要在開機時開啟 Linux(TM) 相容性，可以加入這行到 [.filename]#/etc/rc.conf#：

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

在 64-位元的機器上，[.filename]#/etc/rc.d/abi# 會自動載入用來做 64-位元模擬的模組。

Since the Linux(TM) binary compatibility layer has gained support for running both 32- and 64-bit Linux(TM) binaries (on 64-bit x86 hosts), it is no longer possible to link the emulation functionality statically into a custom kernel.

[[linuxemu-libs-manually]]
=== 手動安裝其他程式庫

若有 Linux(TM) 應用程式在設定 Linux(TM) Binary 相容性後出現缺少共用程式庫的情況，確認這個 Linux(TM) Binary 需要哪個共用程式庫並手動安裝。

在 Linux(TM) 系統，可使用 `ldd` 來找出應用程式需要哪個共用程式庫。 例如，檢查 `linuxdoom` 需要哪個共用程式庫，在有安裝 Doom 的 Linux(TM) 系統執行這個指令：

[source,shell]
....
% ldd linuxdoom
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(TM) 系統輸出結果中最後一欄的檔案到 FreeBSD 系統的 [.filename]#/compat/linux#。 複製完後，建立符號連結 (Symbolic link) 至輸出結果第一欄的名稱。以這個例子會在 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
....

若 Linux(TM) 共用程式庫已經存在，並符合 `ldd` 輸出結果第一欄的主要修訂版號，則不需要複製該行最後一欄的檔案，使用既有的程式庫應可運作。若有較新的版本建議仍要複製共用程式庫，只要符號連結指向新版的程式庫，舊版便可移除。

例如，以下程式庫已存在 FreeBSD 系統：

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

且 `ldd` 顯示 Binary 需要使用較新的版本：

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

雖然既有的程式庫只有在最後一碼過時一或兩個版本，程式應該仍可使用稍微舊的版本執行，雖然如此，保險起見還替換既有的 [.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(TM) 程式到 FreeBSD 完的前幾次會需要查看 Linux(TM) Binary 相依的共用程式庫。之後系統便有足夠的 Linux(TM) 共用程式庫能夠執行新安裝的 Linux(TM) Binary，便不再需要額外的動作。

=== 安裝 Linux(TM)ELF Binary

ELF Binary 有時候需要額外的步驟。當執行無商標 (Unbranded) 的 ELF Binary，會產生錯誤訊息：

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

要協助 FreeBSD 核心區別是 FreeBSD ELF Binary 還是 Linux(TM) Binary，可使用 man:brandelf[1]：

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

由於 GNU 工具鏈會自動放置適當的商標資訊到 ELF Binary，通常不需要這個步驟。

=== 安裝以 Linux(TM)RPM 為基礎的應用程式

要安裝 Linux(TM)RPM 為基礎的應用程式，需先安裝 package:archivers/rpm4[] 套件或 Port。安裝完成之後，`root` 可以使用這個指令安裝 [.filename]#.rpm#：

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

如果需要， `brandelf` 已安裝的 ELF Binary。注意，這將會無法乾淨地解除安裝。

=== 設定主機名稱解析器

如果 DNS 無法運作或出現這個錯誤：

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

將 [.filename]#/compat/linux/etc/host.conf# 設定如下：

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

這指定先搜尋 [.filename]#/etc/hosts#，其次為 DNS。 當 [.filename]#/compat/linux/etc/host.conf# 不存在， Linux(TM) 應用程式會使用 [.filename]#/etc/host.conf# 並會警告不相容的 FreeBSD 語法。如果名稱伺服器未設定使用 [.filename]#/etc/resolv.conf# 的話，則可移除 `bind`。

[[linuxemu-advanced]]
== 進階主題

This section describes how Linux(TM) binary compatibility works and is based on an email written to http://lists.FreeBSD.org/mailman/listinfo/freebsd-chat[FreeBSD chat mailing list] by Terry Lambert mailto:tlambert@primenet.com[tlambert@primenet.com] (Message ID: `<199906020108.SAA07001@usr09.primenet.com>`).

FreeBSD has an abstraction called an "execution class loader". This is a wedge into the man:execve[2] system call.

Historically, the UNIX(TM) loader examined the magic number (generally the first 4 or 8 bytes of the file) to see if it was a binary known to the system, and if so, invoked the binary loader.

If it was not the binary type for the system, the man:execve[2] call returned a failure, and the shell attempted to start executing it as shell commands. The assumption was a default of "whatever the current shell is".

Later, a hack was made for man:sh[1] to examine the first two characters, and if they were `:\n`, it invoked the man:csh[1] shell instead.

FreeBSD has a list of loaders, instead of a single loader, with a fallback to the `#!` loader for running shell interpreters or shell scripts.

For the Linux(TM)ABI support, FreeBSD sees the magic number as an ELF binary. The ELF loader looks for a specialized _brand_, which is a comment section in the ELF image, and which is not present on SVR4/Solaris(TM) ELF binaries.

For Linux(TM) binaries to function, they must be _branded_ as type `Linux` using man:brandelf[1]:

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

When the ELF loader sees the `Linux` brand, the loader replaces a pointer in the `proc` structure. All system calls are indexed through this pointer. In addition, the process is flagged for special handling of the trap vector for the signal trampoline code, and several other (minor) fix-ups that are handled by the Linux(TM) kernel module.

The Linux(TM) system call vector contains, among other things, a list of `sysent[]` entries whose addresses reside in the kernel module.

When a system call is called by the Linux(TM) binary, the trap code dereferences the system call function pointer off the `proc` structure, and gets the Linux(TM), not the FreeBSD, system call entry points.

Linux(TM) mode dynamically _reroots_ lookups. This is, in effect, equivalent to `union` to file system mounts. First, an attempt is made to lookup the file in [.filename]#/compat/linux/original-path#. If that fails, the lookup is done in [.filename]#/original-path#. This makes sure that binaries that require other binaries can run. For example, the Linux(TM) toolchain can all run under Linux(TM)ABI support. It also means that the Linux(TM) binaries can load and execute FreeBSD binaries, if there are no corresponding Linux(TM) binaries present, and that a man:uname[1] command can be placed in the [.filename]#/compat/linux# directory tree to ensure that the Linux(TM) binaries cannot tell they are not running on Linux(TM).

In effect, there is a Linux(TM) kernel in the FreeBSD kernel. The various underlying functions that implement all of the services provided by the kernel are identical to both the FreeBSD system call table entries, and the Linux(TM) system call table entries: file system operations, virtual memory operations, signal delivery, and System V IPC. The only difference is that FreeBSD binaries get the FreeBSD _glue_ functions, and Linux(TM) binaries get the Linux(TM)_glue_ functions. The FreeBSD _glue_ functions are statically linked into the kernel, and the Linux(TM)_glue_ functions can be statically linked, or they can be accessed via a kernel module.

Technically, this is not really emulation, it is an ABI implementation. It is sometimes called "Linux(TM) emulation" because the implementation was done at a time when there was no other word to describe what was going on. Saying that FreeBSD ran Linux(TM) binaries was not true, since the code was not compiled in.
