プログラマの自己啓発ノート: 第 8 章

第 8 章: Linux 共有ライブラリの構成

1. 共有ライブラリのバージョンが異なると ABI が変更され、プログラムの通常の動作に影響を与える可能性があります。したがって、各システムには独自の共有ライブラリのバージョン命名規則のセットが存在します。Linux の場合、
libname.so.xyz
x: メジャー バージョン番号、y: マイナー バージョン番号、z: リリース バージョン番号
共有ライブラリは、メジャー バージョンごとに大きく異なり、相互に互換性がありません。
マイナー バージョン間では新しいシンボルのみが追加され、相互に互換性があります。
インターフェースのバグは、リリースされたバージョン間で互換性を持たせるために変更されています。

2. プログラムが依存する共有ライブラリは、.dynamic セクションで指定されます。完全なバージョン (プライマリ リリースとセカンダリ リリースの両方) の共有ライブラリの名前が指定されている場合は、プログラムを実行するたびに以前のバージョンを保存する必要があります。共有ライブラリが更新されると、ディスクとメモリが同時にロードされます。共有ライブラリの異なるバージョンには大きな負荷がかかるため、Linux は SO-NAME 命名規則を開発しました。
libname.so.x
x は、共有ライブラリのメジャー バージョン番号です。
システムは、各共有ライブラリが配置されているディレクトリに SO-NAME と同じ名前のソフト リンクを作成し、共有ライブラリの最新バージョン (同じメジャー バージョン番号を持つ) を指します。共有ライブラリには複数のメジャー バージョンがインストールされており、対応する複数のソフト接続が存在します。

Linux には「ldconfig」ツールがあり、システムが共有ライブラリをインストールまたは更新するときに、デフォルトの共有ライブラリ ディレクトリをたどって、対応するソフト リンクを更新または作成します。

gcc -lXXX は XXX 共有ライブラリへのリンクを意味し、システムは最新バージョンの XXX ライブラリ (多くの場合 -L で指定) を検索します。-lc を使用すると、出力ファイルに従ってライブラリの適切なバージョン (静的または動的) が選択されます。

3. SO-NAMEによるマイナーバージョン番号交差問題とシンボルバージョン機構の解決

アプリケーションが lib.so のバージョン 2.5 に依存しており、システムにバージョン 2.3 しかない場合、プログラムは同じ SO-NAME のため正常に実行されますが、2.5 で追加されたシンボルを参照すると異常終了します。これはバージョン番号の交差に関するマイナーな問題です。

シンボリック バージョンはこの前提のもとに導入されました。共有ライブラリ ビルドのリンク プロセスでは、書き込まれたシンボル バージョン スクリプトをリンクできます。スクリプトにはいくつかのコレクションがあります。コレクションには、VERS_1.1 などの独自の名前があります。コレクションには、共有ライブラリによって定義されたシンボルが含まれているため、共有ライブラリのシンボルには独自のバージョン (シンボルが属するコレクション名) があります。 )。同時に、コレクション間に継承関係が存在する場合があります。たとえば、VERS_1.2 は VERS_1.1 から継承するため、共有ライブラリの構築後の .so ファイルには、コレクションの関連コンテンツが含まれます。
アプリケーションが共有ライブラリ lib.so にリンクすると、アプリケーションが依存する共有ライブラリの最も低いシンボル セット バージョンがプログラムの最終出力ファイル (多くの場合、実行可能ファイル) に記録されます。システムが所有していない、最高のバージョン。
プログラムの実行中、ダイナミック リンカーは、プログラムによって記録された、依存するすべての共有ライブラリ シンボルのセット バージョンを通じて、システムによってインストールされた対応する共有ライブラリが要件を満たしているかどうかを判断できます。満たしていない場合は、プログラムの実行が妨げられます。 。
シンボリック バージョン メソッドは、SO-NAME メカニズムを補完するものです。

----Linux シンボル バージョン
Linux でのシンボル バージョン メカニズムは広く使用されておらず、主に glibc の下でいくつかの共有ライブラリを使用します。
Linux では、Solaris シンボル バージョンに 2 つの拡張機能があります。


シンボル バージョン スクリプトでシンボルのバージョンを指定するだけでなく、 asm(".symversymbolInCode,symbolInScript@VERS_1.1")を使用して C/C++ ソース コードの
シンボル タグにシンボルを追加できます。

GCC では、リンク レベルでのシンボル オーバーロード メカニズムにより、同じシンボルが複数のバージョンの共有ライブラリに存在することができます。
このオーバーロード メカニズムの利点は、インターフェイスまたは少数の既存インターフェイスの意味が変更された場合に、古いバージョンの機能が影響を受けないようにするためにメイン バージョンをアップグレードする必要がないことです。
asm(".symver newprintf,printf@VERS_1.1")
asm(".symver oldprintf,printf@VERS_1.1")
void oldprintf(){}
void newprintf(){}

4. 共有ライブラリのシステム パス
/usr/lib
/lib は通常、システム自体に必要なライブラリであり、
/usr/local/lib は通常、システムに必要のないサードパーティ プログラムの共有ライブラリです。

5. 共有ライブラリの検索処理
共有ライブラリの検索は、動的にリンクされたモジュールの「.dynamic」セクション内の DT_NEED タイプの項目に基づいて、ダイナミック リンカ (/lib/ld-linux.so.x) によって実行されます。
値が絶対パスの場合は絶対パスで検索され、相対パスの場合は/lib、/usr/lib、/etc/ld.soで指定されたディレクトリ内で検索されます。 conf 設定ファイル。共有ライブラリの移植性を考慮して、多くの場合、共有ライブラリのパスは相対パスになります。
ld がこれらのディレクトリを毎回走査すると、非常に時間がかかります。したがって、上記の ldconfig というプログラムがあり、上記の共有ライブラリを担当する SO-NAME ソフト リンクに加えて、これらの SO-NAME の特別なデータ構造も収集し、/etc/ld.so.cache に保存します。 、それによってプロセスがスピードアップします。キャッシュ内に ld が見つからない場合は、/lib と /usr/lib を走査しますが、それ以外の場合は失敗します。
したがって、理論的には、共有ライブラリが追加、削除、または更新されるたびに ldconfig を実行する必要があり、多くのソフトウェア パッケージは、共有ライブラリのインストール後にこれを呼び出します。

6.3 ダイナミック リンクに関連する環境変数
LD_LIBRARY_PATH は
、共有ライブラリを検索する ld のプロセスを変更し、最初にこの変数で指定されたパスをクエリし、次にデフォルトで検索します。共有ライブラリのオープンなデバッグとテストを容易にします。乱用してはいけません。また、gcc のコンパイルと検索ライブラリのパスに影響を与え、その効果は gcc の -L パラメータと同等です。
LD_LIBRARY_PATH=/home/mary:/home/ben /bin/ls

LD_PRELOAD は、
指定された共有ライブラリを事前にロードします。これは、前の変数よりも高い優先順位を持ちます。グローバル シンボル介入メカニズムの存在により、標準 C ライブラリの 1 つまたは複数の関数を他の関数に影響を与えることなく簡単に書き換えることができるため、デバッグやテストに役立ちます。
(/etc/ld.so.preload はこの変数と同じ効果があります)

LD_DEBUGの値に応じて
、動的リンカーは実行時にさまざまな有用な情報を出力します。
LD_DEBUG=ファイル ./helloworld.out

7. 共有ライブラリの作成
gcc でコンパイルする場合、-fPIC、-shared パラメータに加えて、出力共有ライブラリの SO-NAME を指定するためにリンカに渡される -W1、-so-name、mysoname パラメータもあります。 。
このパラメータを使用しない場合、共有ライブラリにはデフォルトで SO-NAME が設定されないため、ldconfig を使用して SO-NAME ソフト リンクを更新しても、共有ライブラリには影響しません。

注:
出力共有ライブラリのシンボルとデバッグ情報を削除しないでください。また、gcc の「-fomit-frame-pointer」を使用してスタック フレーム オプションを削除しないでください。共有ライブラリのデバッグに影響します。
既存のプログラムに影響を与えることなく、新しい共有ライブラリをテストします。LD_LIBRARY_PATH の使用に加えて、ld の "-rpath" オプションを使用して、ターゲット プログラムの共有ライブラリの検索パスを指定することもできます。
デフォルトでは、リンカーによって生成された実行可能ファイルでは、メイン モジュール内の他の共有モジュールによって参照されるシンボルのみが動的シンボル テーブルに配置され、動的シンボル テーブルのサイズが削減されます。これにより、プログラムが dlopen を使用して共有モジュールを動的にロードし、メイン モジュールからシンボルを参照する必要がある場合、シンボルが動的シンボル テーブルにないために後方参照が失敗するという状況が発生します。リンカは、上記の問題を防ぐために、実行可能ファイル (メイン モジュール) 内のすべてのグローバル シンボルを動的シンボル テーブルにエクスポートするための「-export-dynamic」パラメータを提供します。

リリース バージョンでは、シンボルはほとんど効果がありません。「ストリップ」ツールを使用して、共有ライブラリまたは実行可能ファイルのすべてのシンボルとデバッグ情報、または ld の「-s」または「-S」パラメータをクリアできます。
ストリップリブフー.so

システムの root 権限が必要な方法:
共有ライブラリを /lib、/usr/lib、およびその他のシステム ディレクトリにコピーし、ldconfig を実行します。
root 権限を必要としない方法:
ldconfig を使用して SO-NAME を作成します (ダイナミック リンカーが見つけるため)
ldconfig -nshared_Library_directory
。また、プログラムをコンパイルするときに共有ライブラリの場所を指定する必要もあります (-L と -l、または-rpath パラメータ (静的リンク) デバイスが必要)

10. gcc における共有ライブラリのコンストラクタ関数とデストラクタ関数は
属性((constructorx))
属性((destructorx)) で指定し、x は優先度を指定しますが省略可能です。コンストラクター内の x が小さいほど優先度が高く、デストラクター内の x が小さいほど優先度が低くなります。これは、リソースが最初に適用され、後で解放されるという原則と一致します。
この方法による共有ライブラリの構築では、gcc の「-nostartfiles」または「-nostdlib」パラメータを使用できません。これらのコンストラクタとデストラクタはシステムのデフォルトの標準ランタイム ライブラリまたはスタートアップ ファイルで実行されるためです。これらの補助結果がなければ実行されません。

11. 共有ライブラリ スクリプトは、
複数の既存の共有ライブラリを結合して、ユーザーの観点から新しい共有ライブラリを作成できます。
GROUP (/lib/libc.so.6 /lib/libm.so.2)
構文コマンドはリンク スクリプトと何ら変わりはなく、その機能も似ており、1 つ以上の入力ファイルを特定の形式に変換して、出力ファイル。そのため、共有ライブラリ スクリプトはダイナミック リンク スクリプトとも呼ばれ、リンク プロセスは実行時に完了します。

おすすめ

転載: blog.csdn.net/weixin_45719581/article/details/123207565