102.3 レッスン 1
Certificate: |
LPIC-1 |
---|---|
Version: |
5.0 |
Topic: |
102 Linuxのインストールとパッケージ管理 |
Objective: |
102.3 共有ライブラリの管理 |
Lesson: |
1 of 1 |
はじめに
このレッスンでは、共有ライブラリ(共有オブジェクト と呼ぶこともある)について解説します。これは、さまざまなプログラムから利用される、関数やクラスなどの再利用可能なコンパイル済みコードのことです。
まず、共有ライブラリとは何で、どこにあるのか、どうやって見分けるのかについて説明します。次に、それらの保存場所を設定する方法を説明します。最後に、プログラムが利用する共有ライブラリを調べる方法を示します。
共有ライブラリの概念
ソフトウェア・ライブラリは、物理的な部品と同様に、様々なプログラムで利用されるコードの集合体です。例えれば、様々な人に利用されるように、図書館が本や資料を保管しているのと同じです。
プログラムのソースコードから実行可能ファイルを作成するには、2つのステップが必要です。まず、コンパイラ がソースコードをマシンコードに変換して、オブジェクトファイル に保存します。次に、リンカー がオブジェクトファイルとライブラリを リンク(取りまとめ)して、最終的な実行可能ファイルを生成します。このリンクには、静的 な方法と、動的 な方法があります。静的リンクの時には静的ライブラリの話になりますし、動的リンクの時には共有ライブラリの話になります。それらの違いを説明しましょう。
- 静的ライブラリ
-
静的ライブラリはリンク時にプログラムと結合されます。ライブラリコードのコピーがプログラムに埋め込まれて、プログラムの一部になります。つまり、プログラムとライブラリコードが一体化しているので、プログラムの実行時にはライブラリファイルを必要としません。使用されているライブラリファイルが利用可能であることを確認する必要がなく、プログラム単体で実行できることが利点となります。対して、静的にリンクされたプログラムのファイルサイズが大きくなることが欠点です。
- 共有(動的)ライブラリ
-
共有ライブラリの場合、リンカはプログラムがライブラリを正しく参照できるように調整するだけです。リンカはライブラリコードをプログラムファイルに埋め込みません。そのため、プログラムの実行時には、共有ライブラリを参照してプログラムに欠けた部分を補う必要があります。これにより、プログラムファイルのサイズが小さくなりますし、複数のプログラムが同じライブラリを使用している場合でもメモリにロードされるのは1つのライブラリコードだけですから、システムリソースを効率的に利用することができます。
共有オブジェクトファイルの命名規則
共有ライブラリの名前は、次の3つの要素で構成されます。
-
ライブラリ名(通常はプレフィックスが
lib
) -
so
(“shared object” の略) -
ライブラリのバージョン番号
例を示します: libpthread.so.0
対する静的ライブラリの名前は .a
で終わります(例: libpthread.a
)。
Note
|
プログラムの実行には共有ライブラリのファイルが必要であるため、ほとんどすべてのLinuxシステムには共有ライブラリが含まれています。静的ライブラリは、プログラムをリンクする時にのみ必要なので、エンドユーザー用のシステムに含まれないのが一般的です。 |
glibc
(GNU Cライブラリ)は共有ライブラリの良い例です。Debian GNU/Linux 9.9システムでは、そのファイルの名前は libc.so.6
です。このようなシンプルなファイル名は通常、ライブラリファイルの実体を指すシンボリックリンクです。ライブラリファイルの実体の名前には、正確なバージョン番号が含まれています。glibc
の例では、次のようなシンボリックリンクです。
$ ls -l /lib/x86_64-linux-gnu/libc.so.6 lrwxrwxrwx 1 root root 12 feb 6 22:17 /lib/x86_64-linux-gnu/libc.so.6 -> libc-2.24.so
このように、正確なバージョンを含む名前の共有ライブラリファイルを、より一般的なファイル名で参照するのはよくあることです。
共有ライブラリの他の例には、libreadline
(入力行の編集機能を提供する。Emacsモードとviモードがある)や、libcrypt
(暗号、ハッシュ、エンコードなどの関数を含む)、libcurl
(マルチプロトコルファイル転送ライブラリ)などが挙げられます。
Linuxシステムでは、一般的に次のディレクトリに共有ライブラリを置きます。
-
/lib
-
/lib32
-
/lib64
-
/usr/lib
-
/usr/local/lib
Note
|
共有ライブラリの概念はLinuxだけのものではありません。たとえば、WindowsではDLL(ダイナミックリンクライブラリ)と呼びます。 |
共有ライブラリパスの設定
動的にリンクされたプログラムを実行する場合には、動的リンカー(ld.so
ないし ld-linux.so
)が、プログラムに埋め込まれたライブラリへの参照を解決します。動的リンカーは、ライブラリパス と呼ばれるいくつかのディレクトリでライブラリを探します。ライブラリパスは、/etc/ld.so.conf
ファイル、ならびに /etc/ld.so.conf.d
ディレクトリにあるファイルで定義します。通常、前者には、後者の *.conf
ファイル群を読み取るための include
行が1つだけ含まれています。
$ cat /etc/ld.so.conf include /etc/ld.so.conf.d/*.conf
/etc/ld.so.conf.d
ディレクトリには、いくつかの *.conf
ファイルが置かれています。
$ ls /etc/ld.so.conf.d/ libc.conf x86_64-linux-gnu.conf
これらの *.conf
ファイルには、共有ライブラリが置かれているディレクトリへの絶対パスが含まれます。
$ cat /etc/ld.so.conf.d/x86_64-linux-gnu.conf # Multiarch support /lib/x86_64-linux-gnu /usr/lib/x86_64-linux-gnu
ldconfig
コマンドは、これらの設定ファイルを読み取って、ライブラリの検索に役立つ一群のシンボリックリンクを作成し、さらにキャッシュファイル /etc/ld.so.cache
を更新します。つまり、設定ファイルを追加ないし更新した場合には、ldconfig
を実行する必要があります。
ldconfig
の主要なオプションを示します:
-v
,--verbose
-
ライブラリのバージョン番号、ディレクトリ名、作成したリンクを表示する。
$ sudo ldconfig -v /usr/local/lib: /lib/x86_64-linux-gnu: libnss_myhostname.so.2 -> libnss_myhostname.so.2 libfuse.so.2 -> libfuse.so.2.9.7 libidn.so.11 -> libidn.so.11.6.16 libnss_mdns4.so.2 -> libnss_mdns4.so.2 libparted.so.2 -> libparted.so.2.0.1 (...)
この例では
libfuse.so.2
が、共有ライブラリファイルの実体libfuse.so.2.9.7
を参照していることが分かります。 -p
,--print-cache
-
キャッシュに保存されているライブラリとディレクトリのリストを表示します。
$ sudo ldconfig -p 1094 libs found in the cache `/etc/ld.so.cache' libzvbi.so.0 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libzvbi.so.0 libzvbi-chains.so.0 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libzvbi-chains.so.0 libzmq.so.5 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libzmq.so.5 libzeitgeist-2.0.so.0 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libzeitgeist-2.0.so.0 (...)
キャッシュがリンクの完全なso名をどのように利用しているかに着目してください。
$ sudo ldconfig -p |grep libfuse libfuse.so.2 (libc6,x86-64) => /lib/x86_64-linux-gnu/libfuse.so.2
ls
のロングオプション(-l)で /lib/x86_64-linux-gnu/libfuse.so.2
を表示すると、同じディレクトリにある共有ライブラリファイル libfuse.so.2.9.7
へのリンクであることがわかります。
$ ls -l /lib/x86_64-linux-gnu/libfuse.so.2 lrwxrwxrwx 1 root root 16 Aug 21 2018 /lib/x86_64-linux-gnu/libfuse.so.2 -> libfuse.so.2.9.7
Note
|
rootが所有する |
上記の設定ファイルに加えて、LD_LIBRARY_PATH
環境変数で、共有ライブラリの検索パスを一時的に追加できます。環境変数には、ライブラリを検索するディレクトリをコロン(:
)で区切って指定します。たとえば、現在のシェルセッションのライブラリパスに /usr/local/mylib
を追加するには、次のようにします。
$ export LD_LIBRARY_PATH=/usr/local/mylib
次のように値を確認できます。
$ echo $LD_LIBRARY_PATH /usr/local/mylib
LD_LIBRARY_PATH
環境変数を削除するには、次のようにします。
$ unset LD_LIBRARY_PATH
変更を永続的にするには、Bashの初期化スクリプトに次の行を追加します。
export LD_LIBRARY_PATH=/usr/local/mylib
/etc/bash.bashrc
や ~/.bashrc
など、初期化スクリプトのいずれか1つに追加してください。
Note
|
|
実行可能ファイルの依存関係を調べる
あるプログラムが必要とする共有ライブラリを調べるには、ldd
コマンドにプログラムファイルへの絶対パスを与えます。共有ライブラリファイルのパスと、ファイルがロードされるメモリアドレスが16進数で表示されます。
$ ldd /usr/bin/git linux-vdso.so.1 => (0x00007ffcbb310000) libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007f18241eb000) libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f1823fd1000) libresolv.so.2 => /lib/x86_64-linux-gnu/libresolv.so.2 (0x00007f1823db6000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f1823b99000) librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f1823991000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f18235c7000) /lib64/ld-linux-x86-64.so.2 (0x00007f182445b000)
同様に、ldd
を使用して、共有ライブラリの依存関係を調べることもできます。
$ ldd /lib/x86_64-linux-gnu/libc.so.6 /lib64/ld-linux-x86-64.so.2 (0x00007fbfed578000) linux-vdso.so.1 (0x00007fffb7bf5000)
ldd
に -u
(--unused
)オプションを指定すると、プログラムではコードを使用していないが、依存している共有ライブラリを表示します。(訳注: プログラムをビルドする際の問題であり、一般ユーザーやシステム管理者が気にする必要はほとんどありません。)
$ ldd -u /usr/bin/git Unused direct dependencies: /lib/x86_64-linux-gnu/libz.so.1 /lib/x86_64-linux-gnu/libpthread.so.0 /lib/x86_64-linux-gnu/librt.so.1
未使用ライブラリへの不必要な依存関係は、バイナリを構築するときにリンカに指定するオプションが不適切な際に発生します。readelf
や objdump
などのコマンドを使用して調査できます。これらのコマンドを、発展演習で使用します。
演習
-
次の共有ライブラリ名をそれぞれのパーツに分けてみましょう。
ファイル名 ライブラリ名 so サフィックス バージョン番号 linux-vdso.so.1
libprocps.so.6
libdl.so.2
libc.so.6
libsystemd.so.0
ld-linux-x86-64.so.2
-
ソフトウェアを開発したので、その共有ライブラリディレクトリ(
/opt/lib/mylib
)をシステムに追加したいとします。その絶対パスをmylib.conf
というファイルに書き込みました。-
このファイルをどのディレクトリに置きますか?
-
変更を有効にするには、どのコマンドを実行しますか?
-
-
kill
コマンドが必要とする共有ライブラリを表示するコマンドはどうなりますか?
発展演習
-
objdump
は、オブジェクトファイルの情報を表示するユーティリティです。which objdump
を使用して、システムにインストールされていることを確認してください。インストールされていない場合は、インストールしてください。-
objdump
の-p
(--private-headers
)オプションとgrep
を使用して、glibc
の依存関係を表示してください: -
objdump
の-p
(--private-headers
)オプションとgrep
を使用して、glibc
のso名を表示してください: -
objdump
の-p
(--private-headers
)オプションとgrep
を使用して、Bashの依存関係を表示してください:
-
まとめ
このレッスンでは、次のことを学びました。
-
共有(動的)ライブラリとは何か。
-
共有ライブラリと静的ライブラリの違い。
-
共有ライブラリの名前(so名)。
-
/lib
や/usr/lib
など、Linuxシステムで共有ライブラリが置かれるディレクトリ。 -
ダイナミックリンカー
ld.so
(ld-linux.so
)の役割。 -
/etc
にあるld.so.conf
ファイルや、ld.so.conf.d
ディレクトリで、共有ライブラリパスを設定する方法。 -
LD_LIBRARY_PATH
環境変数を使用して共有ライブラリパスを設定する方法。 -
実行可能ファイルと共有ライブラリの依存関係を調べる方法。
このレッスンでは以下のコマンドを使用しました:
ls
-
ディレクトリの内容を一覧表示します。
cat
-
ファイルを連結し、標準出力に表示します。
sudo
-
スーパーユーザーの権限でコマンドを実行します。
ldconfig
-
共有ライブラリに必要なリンクを作成します。
echo
-
環境変数の値を表示します。
export
-
子プロセスに引き渡す環境変数を指定します。
unset
-
環境変数を削除します。
ldd
-
プログラムが依存する共有ライブラリを表示します。
readelf
-
ELF(executable and linkable format)ファイルに関する情報を表示します。
objdump
-
オブジェクトファイルに関する情報を表示します。
演習の解答
-
次の共有ライブラリ名をそれぞれのパーツに分けてみましょう。
ファイル名 ライブラリ名 so サフィックス バージョン番号 linux-vdso.so.1
linux-vdso
so
1
libprocps.so.6
libprocps
so
6
libdl.so.2
libdl
so
2
libc.so.6
libc
so
6
libsystemd.so.0
libsystemd
so
0
ld-linux-x86-64.so.2
ld-linux-x86-64
so
2
-
ソソフトウェアを開発したので、その共有ライブラリディレクトリ(
/opt/lib/mylib
)をシステムに追加したいとします。その絶対パスをmylib.conf
というファイルに書き込みました。-
このファイルをどのディレクトリに置きますか?
/etc/ld.so.conf.d
-
変更を有効にするには、どのコマンドを実行しますか?
ldconfig
-
-
kill
コマンドが必要とする共有ライブラリを表示するコマンドはどうなりますか?ldd /bin/kill
発展演習の解答
-
objdump
は、オブジェクトファイルからの情報を表示するコマンドラインユーティリティです。which objdump
を使用してシステムにインストールされているかどうかを確認してください。そうでない場合は、インストールしてください。-
objdump
の-p
(--private-headers
)オプションとgrep
を使用して、glibc
の依存関係を表示してください:objdump -p /lib/x86_64-linux-gnu/libc.so.6 | grep NEEDED
-
objdump
の-p
(--private-headers
)オプションとgrep
を使用して、glibc
のso名を表示してください:objdump -p /lib/x86_64-linux-gnu/libc.so.6 | grep SONAME
-
objdump
の-p
(--private-headers
)オプションとgrep
を使用して、Bashの依存関係を表示してください:objdump -p /bin/bash | grep NEEDED
-