1. 概要

このチュートリアルでは、 so (共有オブジェクト)ファイルがLinuxファイルシステムでどのように編成されているかを調べます。

2. 例

この記事の文脈をよりよく理解するために、例が必要になります。 これはオペレーティングシステムによって異なる可能性があるため、ldconfigツールを使用して使用できるライブラリを見つけましょう。

$ ldconfig -p
....
libGLX.so.0 (libc6,x86-64) => /lib/x86_64-linux-gnu/libGLX.so.0
libGLU.so.1 (libc6,x86-64) => /lib/x86_64-linux-gnu/libGLU.so.1
libGLESv2.so.2 (libc6,x86-64) => /lib/x86_64-linux-gnu/libGLESv2.so.2
libGL.so.1 (libc6,x86-64) => /lib/x86_64-linux-gnu/libGL.so.1
libFLAC.so.8 (libc6,x86-64) => /lib/x86_64-linux-gnu/libFLAC.so.8
libEGL_mesa.so.0 (libc6,x86-64) => /lib/x86_64-linux-gnu/libEGL_mesa.so.0
libEGL.so.1 (libc6,x86-64) => /lib/x86_64-linux-gnu/libEGL.so.1
libBrokenLocale.so.1 (libc6,x86-64, OS ABI: Linux 3.2.0) => /lib/x86_64-linux-gnu/libBrokenLocale.so.1
libBrokenLocale.so (libc6,x86-64, OS ABI: Linux 3.2.0) => /lib/x86_64-linux-gnu/libBrokenLocale.so
libBLTlite.2.5.so.8.6 (libc6,x86-64) => /lib/libBLTlite.2.5.so.8.6
libBLT.2.5.so.8.6 (libc6,x86-64) => /lib/libBLT.2.5.so.8.6
ld-linux-x86-64.so.2 (libc6,x86-64) => /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2

ldconfig を使用すると、このシステムにインストールされているすべてのライブラリの印刷リストを確認できます。

zipライブラリに行きましょう。

$ ldconfig -p | grep "libzip.so*"
libzip.so.5 (libc6,x86-64) => /lib/x86_64-linux-gnu/libzip.so.5
libzip.so (libc6,x86-64) => /lib/x86_64-linux-gnu/libzip.so

このシステムでは、zipライブラリは/lib/x86_64-linux-gnu/libzip.so*にあります。

zipライブラリが存在しない場合は、リストから別のlibname.soファイルを選択する必要があることに注意してください。

$ ls -l /lib/x86_64-linux-gnu/libzip.so*
lrwxrwxrwx 1 root root 11 Nov 27 2018 /lib/x86_64-linux-gnu/libzip.so -> libzip.so.5
lrwxrwxrwx 1 root root 13 Nov 27 2018 /lib/x86_64-linux-gnu/libzip.so.5 -> libzip.so.5.0
-rw-r--r-- 1 root root 105672 Nov 27 2018 /lib/x86_64-linux-gnu/libzip.so.5.0

そこで、例として libzip を使用して、.so.5.so.5.0などの3つの異なるファイルとその番号について説明します。

3. LinuxでのSOファイル編成

選択した例には、次の3つのファイルがあることがわかります。

/lib/x86_64-linux-gnu/libzip.so
/lib/x86_64-linux-gnu/libzip.so.5
/lib/x86_64-linux-gnu/libzip.so.5.0

これらの各ファイルには特別な名前と用途があります。

libzip.so        # is called the linker-name used for linking.
libzip.so.5      # is called the soname used by the operating system loader.
libzip.so.5.0    # is called the real-name which is updated by the library maintainer.

ここで、「zip」はライブラリの名前です。

各ファイルの末尾には異なる番号が付いています。 これらの番号は、ライブラリのバージョンを表しています。 各ファイルの役割を決定するのはこれらのバージョンであるため、これらは非常に重要です。

これらは2つの人気のあるバージョン管理スキームです。 1つはセマンティックバージョニングです。 もう1つのバリエーションは、Libtoolバージョン管理スキームです。

これらのバージョンスキームについて詳しく説明することから始めて、使用されている命名規則に従ってフォローアップしましょう。 次に、各ファイルの役割について詳しく説明します。

3.1. Linuxのバージョン管理

Linuxの各ライブラリには、ファイル名で記述されたバージョン情報があります。 たとえば、lbizip.so.5.0の汎用形式はlibzip.so.XYZです。 これは、セマンティックバージョニングとも呼ばれ、Linuxシステム上のすべてのライブラリが従う規則です。

Xはメジャーバージョンを表します。 ライブラリ開発者が下位互換性のない変更を行う場合は、メジャーバージョンを増やす必要があります。

Yは、マイナーバージョンを表します。  下位互換性のある変更により、マイナーバージョンがインクリメントされます。 たとえば、新しい関数のような分離された機能を追加します。 また、Z番号が存在しない場合、これはバグ修正を表す場合もあることに注意してください。

Zはパッチ番号です。 これはオプションです。 バグ修正により、パッチバージョンが増加します。

もう1つの重要な側面は、リリースバージョンです。

インストールされているllvmライブラリを簡単に見てみましょう。

$ ldconfig -p | grep LLVM
libLLVM-9.so.1 (libc6,x86-64) => /lib/x86_64-linux-gnu/libLLVM-9.so.1
libLLVM-9.so (libc6,x86-64) => /lib/x86_64-linux-gnu/libLLVM-9.so
libLLVM-8.so.1 (libc6,x86-64) => /lib/x86_64-linux-gnu/libLLVM-8.so.1
libLLVM-8.so (libc6,x86-64) => /lib/x86_64-linux-gnu/libLLVM-8.so

ここには、同じライブラリllvm用のlibLLVM-9とlibLLVM-8の2つのリリースがあることに注意してください。 リリース8での相違は、ライブラリの下位互換性がなくなったことを意味します。 したがって、ライブラリ開発者は、別のリリースlibLLVM-9を作成することにしました。

3.2. Libtoolのバージョン管理

libtoolバージョンスキームをセマンティックバージョニングと混同しないことが重要です。 libtool バージョン管理スキームの目標は、複数のプラットフォーム間でバージョンを標準化することでした。

libtool を使用している開発者は、libtoolバージョンスキームを使用できます。 ただし、これはlibtoolによってLinuxシステムのセマンティックバージョニングに可能な限り最適に変換されます。

セマンティックバージョニングとは対照的です。 libtoolバージョンスキームには、XYZ番号フィールドの現在、リビジョン、および経過時間があります。 これは、メジャー、マイナー、およびパッチの代わりです。 libtool バージョンスキームには、バージョン値をいつインクリメントするかに関する一連のルールも付属しています。

3.3. 命名規則

ldconfig によってリストされたさまざまなライブラリを見ると、規則に気付くことができます。

ldconfig ユーティリティなどは、 so (共有オブジェクト)ファイルが lib * .so*またはld-*。soの命名パターンに従うことを期待しています。 * 後者は、ダイナミックリンカー専用に予約されています。 この規則は、多くのLinuxツールでハードコーディングされています。 これが、ldconfigによってリストされるすべてのライブラリがlibで始まる理由です。

この規則を期待するツールの例は、GCCです。 開発者がGCCを使用してコードをコンパイルしようとすると、ライブラリ名が-lフラグとともに渡されます。

gcc a.c -l zip

リンカは文字列「lib」を取り、「zip」(ライブラリ名)を追加します。 これにより、リンカーが検索しようとするlibzip.soというファイルが作成されます。

結論として、Linux環境のツールは、libname.soの命名規則に従います。

4. SOファイル

各リンカファイルを詳しく見ていきましょう。 まず、実名を見ていきましょう。

実名は、ディスク上の物理ライブラリの実際のファイル名です。ただし、重要な点は、libzipライブラリを次のように拡張する場合です。

# Linker-name
/lib/x86_64-linux-gnu/libzip.so
# sonames
/lib/x86_64-linux-gnu/libzip.so.4
/lib/x86_64-linux-gnu/libzip.so.5
# real-names
/lib/x86_64-linux-gnu/libzip.so.4.1
/lib/x86_64-linux-gnu/libzip.so.4.2
/lib/x86_64-linux-gnu/libzip.so.5.0

Linuxシステム上に複数の物理ライブラリを持つことができます。 彼らは皆、同じフォルダーで幸せに暮らしています。 soname ファイルを作成することにより、アプリケーションを実行するための適切なライブラリを使用できます。 開発者は、linker-name を変更することで、リンクするライブラリを選択することもできます。

残りの概念を詳しく見てみましょう。

5. リンカー名

libzip.so は、番号が関連付けられていないシンボリックリンクです。 リンカー名として知られています。

リンカの検索メカニズムにちなんで名付けられました。 検索メカニズムは、ライブラリ名であるキーを受け取り、libname.soのパターンを持つファイルを返します。

シンボリックリンクであり、バージョンがないため、 libzip.so を使用して、任意のlibzipバージョンを指すことができます。

$ ls -lah /lib/x86_64-linux-gnu/libzip.so
lrwxrwxrwx 1 root root 15 Mar 9 12:45 /lib/x86_64-linux-gnu/libzip.so -> libzip.so.5

この特定の例では、libzip.soは最新のメジャーlibzipバージョンlibzip.so.5を指しています。

デフォルトでは、シンボリックリンクはインストールスクリプトによって設定され、インストールしていたライブラリのバージョンを指します。 リンクするライブラリを変更したい場合は、シンボリックリンクを変更できます。

概念としてのlinker-nameシンボリックリンクは、リンカーが依存できる一貫したインターフェイスを提供します。また、検索およびリンクするライブラリファイルをリンカーに指示する自由も与えられます。

6. soname

sonameを詳しく見てみましょう。 libzip.so.5 は、ldconfigを実行することによって自動的に生成されます。

これがどのように機能するか見てみましょう:

まず、次のようにlibzip.so.5を削除しましょう。

$ sudo rm /usr/lib/x86_64-linux-gnu/libzip.so.5
$ ls -l /usr/lib/x86_64-linux-gnu/libzip*
lrwxrwxrwx 1 root root     33 May 21 16:21 /usr/lib/x86_64-linux-gnu/libzip.so -> /lib/x86_64-linux-gnu/libzip.so.5
-rw-r--r-- 1 root root 105672 Nov 27  2018 /usr/lib/x86_64-linux-gnu/libzip.so.5.0

そのため、libzip.so.5のシンボリックリンクを正常に削除しました。

それでは、ldconfigを実行してみましょう。

$ sudo ldconfig
$ ls -l /usr/lib/x86_64-linux-gnu/libzip*
lrwxrwxrwx 1 root root     33 May 21 16:21 /usr/lib/x86_64-linux-gnu/libzip.so -> /lib/x86_64-linux-gnu/libzip.so.5
lrwxrwxrwx 1 root root     13 May 21 16:26 /usr/lib/x86_64-linux-gnu/libzip.so.5 -> libzip.so.5.0
-rw-r--r-- 1 root root 105672 Nov 27  2018 /usr/lib/x86_64-linux-gnu/libzip.so.5.0

あはは! libzip.so.5が帰ってきました。 実際のライブラリlibzip.so.5.0を指しています。

ここで重要な点は、 ldconfigはどのシンボリックリンクを作成するかをどのように知るのかということです。

libzip.so.5.0ファイルを詳しく見てみましょう。

$ readelf -d /usr/lib/x86_64-linux-gnu/libzip.so.5.0
Dynamic section at offset 0x18db0 contains 28 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libz.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libbz2.so.1.0]
 0x0000000000000001 (NEEDED)             Shared library: [libcrypto.so.1.1]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000e (SONAME)             Library soname: [libzip.so.5]
....

sonameがライブラリのメタデータに書き込まれていることがわかります。 開発者によってlibzip.so.5に設定されています。 symlink libzip.so.5 は、libzipライブラリのメジャーバージョン5を表すライブラリグループです。

したがって、シンボリックリンクは、実際に物理ライブラリlibzip.so.5.0にあるメタデータを反映しています。

これがどのように使用されるかをよりよく理解するために、 ls:を見てみましょう。

$ ldd `which ls`
linux-vdso.so.1 (0x00007ffd0319c000)
libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f02b281d000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f02b262b000)
libpcre2-8.so.0 => /lib/x86_64-linux-gnu/libpcre2-8.so.0 (0x00007f02b259b000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f02b2595000)
/lib64/ld-linux-x86-64.so.2 (0x00007f02b2883000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f02b2572000)

ご覧のとおり、 ldd は、lsの実行時の依存関係を一覧表示します。 最初の列には、共有オブジェクトのsonamesが含まれています。 2番目の列は、共有オブジェクトへのパスと、それがロードされたアドレスです。

例としてlibpthread.so.0を取り上げ、このエントリがどのように追加されたかを考えてみましょう。

$ ls -l /usr/lib/x86_64-linux-gnu/libpthread*
-rwxr-xr-x 1 root root  157224 Apr 14 20:26 /usr/lib/x86_64-linux-gnu/libpthread-2.31.so
-rw-r--r-- 1 root root 6587378 Apr 14 20:26 /usr/lib/x86_64-linux-gnu/libpthread.a
lrwxrwxrwx 1 root root      37 Apr 14 20:26 /usr/lib/x86_64-linux-gnu/libpthread.so -> /lib/x86_64-linux-gnu/libpthread.so.0
lrwxrwxrwx 1 root root      18 Apr 14 20:26 /usr/lib/x86_64-linux-gnu/libpthread.so.0 -> libpthread-2.31.so

ls をコンパイルするとき、リンカーはlibpthread.solinker-nameシンボリックリンクに従いました。 これにより、 libpthread-2.31.so のメタデータが表示され、リンカーが実際のライブラリlibpthread-2.31.soからsonameメタデータを抽出しました。

$ readelf -d libpthread-2.31.so | grep soname
0x000000000000000e (SONAME)             Library soname: [libpthread.so.0]

このメタデータには、実行時の依存関係のリストに追加された soname libpthread.so.0が含まれています。

これは、sonameの主な目的がアプリケーションの実行に使用されることを示しています。ダイナミックリンカーは、sonameを使用してこれらのファイルをメモリにロードします。 このようにして、開発者は実行時の依存関係を壊すことなく、バックグラウンドでライブラリを更新できます。

7. 結論

このチュートリアルは、Linuxのライブラリのファイル命名規則を確認することから始めました。

次に、ライブラリに関連する3つの異なるシンボリックリンクについて詳しく説明しました。 これには、リンカー名、soname、および実名が含まれます。