Linuxプログラムコンパイルリンク動的ライブラリバージョンに関する問題の解決

9920 ワード

前言
異なるバージョンのダイナミックライブラリは互換性がない場合があります.プログラムがコンパイル時にダイナミックライブラリが低バージョンであることを指定し、実行が高バージョンであることを指定すると、実行できない可能性があります.Linux上の動的ライブラリの命名はlibxxxを採用する.so.a.b.cのフォーマットで、aは大バージョン番号、bは小バージョン番号、cはより小さなバージョン番号を表します.Linuxが持参したcpプログラムを例にlddで依存するダイナミックライブラリを表示します.

 $ ldd /bin/cp            
linux-vdso.so.1 => (0x00007ffff59df000)
libselinux.so.1 => /lib64/libselinux.so.1 (0x00007fb3357e0000)
librt.so.1 => /lib64/librt.so.1 (0x00007fb3355d7000)
libacl.so.1 => /lib64/libacl.so.1 (0x00007fb3353cf000)
libattr.so.1 => /lib64/libattr.so.1 (0x00007fb3351ca000)
libc.so.6 => /lib64/libc.so.6 (0x00007fb334e35000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007fb334c31000)
/lib64/ld-linux-x86-64.so.2 (0x00007fb335a0d000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fb334a14000)

左は依存するダイナミックライブラリの名前で、右はリンクが指すファイルで、libaclを表示します.so関連ダイナミックライブラリ

 $ ll /lib64/libacl.so*           
lrwxrwxrwx. 1 root root 15 1  7 2015 /lib64/libacl.so.1 -> libacl.so.1.1.0
-rwxr-xr-x. 1 root root 31280 12  8 2011 /lib64/libacl.so.1.1.0

我々はlibaclを見つけたso.1は実際にはlibaclを指すソフトリンクです.so.1.1.0、命名方法は私たちの上の説明に合っています.このように命名されていないものもあります.例えば

$ ll /lib64/libc.so*            
lrwxrwxrwx 1 root root 12 8  12 14:18 /lib64/libc.so.6 -> libc-2.12.so

いずれにしても、ダイナミックライブラリを所定の方法で生成して使用すれば問題ありません.また,我々は往々にしてマシンA上でプログラムをコンパイルし,マシンB上でプログラムを実行するが,コンパイルと実行の環境は実は少し異なる.次に、ダイナミックライブラリの生成と使用中のいくつかの問題について説明します.
ダイナミックライブラリのコンパイル
簡単なプログラムを例に挙げます

// filename:hello.c
#include 

void hello(const char* name)
{
 printf("hello %s!
", name); } // filename:hello.h void hello(const char* name);

次のコマンドでコンパイル

gcc hello.c -fPIC -shared -Wl,-soname,libhello.so.0 -o libhello.so.0.0.1

注意すべきパラメータは-Wl,soname(真ん中にスペースはありません)、-Wlオプションはコンパイラに後のパラメータをリンクに渡すように伝え、-sonameはダイナミックライブラリのsoname(単純共有名、Short for shared ject obname)を指定します.
私たちはlibhelloを生成しましたso.0.0.1,ldconfig -n .コマンドを実行すると、現在のディレクトリに1つのソフト接続が追加されます.

 $ ll libhello.so.0            
lrwxrwxrwx 1 handy handy 17 8  17 14:18 libhello.so.0 -> libhello.so.0.0.1

このソフトリンクはどのように生成されたのか、libhelloを切り取るわけではありません.so.0.0.1名前の前の部分ではなくlibhello.so.0.0.1コンパイル時に指定した-sonameで生成されます.すなわち,動的ライブラリをコンパイルする際に−sonameで指定した名前は,動的ライブラリのバイナリデータに記載されている.プログラムがlibxxxを押すかどうかにかかわらず.so.a.b.c形式で名前が付けられていますが、Linuxではほとんどのダイナミックライブラリがコンパイル時に-sonameを指定しています.readelfツールでsonameを表示できます.たとえば、冒頭に列挙した2つのダイナミックライブラリなどです.

 $ readelf -d /lib64/libacl.so.1.1.0                     

Dynamic section at offset 0x6de8 contains 24 entries:
Tag Type    Name/Value
0x0000000000000001 (NEEDED)  Shared library: [libattr.so.1]
0x0000000000000001 (NEEDED)  Shared library: [libc.so.6]
0x000000000000000e (SONAME)  Library soname: [libacl.so.1]

ここでは一部を省略する、最後の行SONAMEがlibaclであることがわかる.so.1、だから/lib 64はこのようなソフト接続があります
libc-2.12を見てください.soファイル、このファイルは私たちが言ったネーミング方式を採用していません

 $ readelf -d /lib64/libc-2.12.so                     

Dynamic section at offset 0x18db40 contains 27 entries:
Tag Type    Name/Value
0x0000000000000001 (NEEDED)  Shared library: [ld-linux-x86-64.so.2]
0x000000000000000e (SONAME)  Library soname: [libc.so.6]

同様に最後の行SONAMEがlibcであることがわかる.so.6.ダイナミックライブラリがバージョン番号で命名されていない場合でも、古いソフトチェーンがダイナミックライブラリを指しています.このソフトチェーンの名前はsonameが指定した名前です.
だから肝心なのはこのsonameで、それは1つの中間者に相当して、私達のダイナミックライブラリがただ1つの小さいバージョンをアップグレードする時、私達はそのsonameを同じにすることができて、実行可能なプログラムはsonameが指定したダイナミックライブラリだけを認めて、このようにこのダイナミックライブラリに依存する実行可能なプログラムは再コンパイルする必要がなくて新しいダイナミックライブラリの特性を使用することができます
実行可能プログラムのコンパイル
helloダイナミックライブラリを例に簡単なプログラムを書きます

// filename:main.c
#include "hello.h"

int main()
{
 hello("handy");
 return 0;
}

現在、ディレクトリの下には次の構造があります.

├── hello.c
├── hello.h
├── libhello.so.0 -> libhello.so.0.0.1
├── libhello.so.0.0.1
└── main.c

libhello.so.0.0.1は私たちがコンパイルして生成したダイナミックライブラリです.libhello.so.0はldconfigで生成するリンクであり、以下のコマンドでmainをコンパイルする.c

 $ gcc main.c -L. -lhello -o main                      
/usr/bin/ld: cannot find -lhello

エラーを報告するとhelloダイナミックライブラリが見つかりません.Linuxの下で、コンパイル時に-lhelloを指定すると、リンクはlibhelloを探します.soのようなファイルは、現在のディレクトリの下にこのファイルがないので、エラーを報告します.このようなソフトチェーンを構築し、ディレクトリ構造は以下の通りです.

├── hello.c
├── hello.h
├── libhello.so -> libhello.so.0.0.1
├── libhello.so.0 -> libhello.so.0.0.1
├── libhello.so.0.0.1
└── main.c

いいわよsoリンクは実際のダイナミックライブラリファイルlibhelloを指す.so.0.0.1,再コンパイルmainプログラム

gcc main.c -L. -lhello -o main

これにより実行可能ファイルが生成されます.以上のテストにより、実行可能なプログラムをコンパイルするとき、リンクは依存するlibxxxを探しに行くことが分かった.soこのような書類はlibxxxを保証しなければならない.soの存在
依存するダイナミックライブラリをlddで表示する

 $ ldd main                        
 linux-vdso.so.1 => (0x00007fffe23f2000)
 libhello.so.0 => not found
 libc.so.6 => /lib64/libc.so.6 (0x00007fb6cd084000)
 /lib64/ld-linux-x86-64.so.2 (0x00007fb6cd427000)

mainプログラムに依存するダイナミックライブラリの名前はlibhelloであることが分かった.so.0、libhelloでもない.それはlibhelloではありませんso.0.0.1.実はmainプログラムを生成する過程で次のようなステップがあります.
  • リンクはコンパイルコマンド-L.-lhelloによって現在のディレクトリでlibhelloを検索する.soファイル
  • libhelloを読み出す.soリンクはlibhello.so.0.0.1
  • libhelloを読み出す.so.0.0.1の中のSONAME、ここはlibhelloです.so.0
  • libhello.so.0 mainプログラムのバイナリデータに記録する
  • つまりlibhello.so.0はmainプログラムのバイナリデータに格納されており、このプログラムがどこにあるかにかかわらずlddで依存するダイナミックライブラリを表示するのはlibhelloである.so.0
    なぜここでlddはmainを見てlibhelloを表示しますか?so.0はnot foundですね.lddは環境変数$LDからです.LIBRARY_PATHが指定したパスでファイルを検索する場合は、環境変数を指定して次のように実行します.
    
     $ export LD_LIBRARY_PATH=. && ldd main                    
     linux-vdso.so.1 => (0x00007fff7bb63000)
     libhello.so.0 => ./libhello.so.0 (0x00007f2a3fd39000)
     libc.so.6 => /lib64/libc.so.6 (0x00007f2a3f997000)
     /lib64/ld-linux-x86-64.so.2 (0x00007f2a3ff3b000)

    実行可能プログラムの実行
    テストディレクトリの結果は次のとおりです.
    
    ├── hello.c
    ├── hello.h
    ├── libhello.so -> libhello.so.0.0.1
    ├── libhello.so.0 -> libhello.so.0.0.1
    ├── libhello.so.0.0.1
    ├── main
    └── main.c

    ここではコンパイル環境と実行環境を混ぜましたが、大丈夫です.その原理を知っていれば、それを明らかにすることができます.
    先にlddでmainプログラム依存のダイナミックライブラリを表示し、LD_を指定しました.LIBRARY_PATH変数は、今すぐ実行できます
    
     $ ./main                        
    hello Handy!

    順調に見えます.では、実行環境を導入するには、どのように配置すればいいのでしょうか.明らかに、ソースコードは必要ありません.ダイナミックライブラリと実行可能プログラムしか必要ありません.ここに実行ディレクトリを新規作成し、関連ファイルをコピーします.ディレクトリ構造は次のとおりです.
    
    ├── libhello.so.0.0.1
    └── main

    実行するとmainが見つかります
    
     $ ./main                        
    ./main: error while loading shared libraries: libhello.so.0: cannot open shared object file: No such file or directory

    言い間違えたso.0ファイルが見つかりません.つまり、プログラムの実行時に検索する必要があるダイナミックライブラリファイル名は、ダイナミックライブラリのコンパイル時に指定されたSONAMEであり、lddで表示したものと一致します.ldconfig -n .によってリンクが確立され、以下のようになります.
    
    ├── libhello.so.0 -> libhello.so.0.0.1
    ├── libhello.so.0.0.1
    └── main

    プログラムを再実行すると、結果は予想に合致します.
    以上のテストから、プログラムが実行されている間にlibxxxを知る必要はありません.soではなく、プログラム自体に記載されているこのダイナミックライブラリのSONAMEが必要なので、mainプログラムの実行環境は以上の3つのファイルだけでよい
    ダイナミックライブラリバージョンの更新
    ダイナミックライブラリには、次のような小さな変更が必要です.
    
    // filename:hello.c
    #include 
    
    void hello(const char* name)
    {
     printf("hello %s, welcom to our world!
    ", name); }

    変更が小さいため、ダイナミックライブラリをコンパイルするときも同じsonameを指定します.
    
    gcc hello.c -fPIC -shared -Wl,-soname,libhello.so.0 -o libhello.so.0.0.2

    新しいダイナミックライブラリを実行ディレクトリにコピーします.このとき、実行ディレクトリ構造は次のようになります.
    
    ├── libhello.so.0 -> libhello.so.0.0.1
    ├── libhello.so.0.0.1
    ├── libhello.so.0.0.2
    └── main

    ディレクトリの下に2つのバージョンのダイナミックライブラリがありますがlibhello.so.0は古いバージョンを指し、ldconfig -n .を実行した後、リンクは新しいバージョンを指していることがわかりました.以下のようにします.
    
    ├── libhello.so.0 -> libhello.so.0.0.2
    ├── libhello.so.0.0.1
    ├── libhello.so.0.0.2
    └── main

    さいじっこうプログラム
    
     $ ./main                        
    hello Handy, welcom to our world!

    再コンパイルせずに新しいダイナミックライブラリを使用しましたwonderful!
    同様に、ダイナミックライブラリに大きな変更がある場合は、ダイナミックライブラリをコンパイルするときに新しいsonameを指定します.
    
    gcc hello.c -fPIC -shared -Wl,-soname,libhello.so.1 -o libhello.so.1.0.0

    動的ライブラリファイルを実行ディレクトリにコピーし、次のように構成されたldconfig -n . を実行します.
    
    ├── libhello.so.0 -> libhello.so.0.0.2
    ├── libhello.so.0.0.1
    ├── libhello.so.0.0.2
    ├── libhello.so.1 -> libhello.so.1.0.0
    ├── libhello.so.1.0.0
    └── main

    新しいリンクが生成されましたso.1、mainプログラムはlibhelloを使用しています.so.0のため、新しいダイナミックライブラリの機能は使用できません.再コンパイルする必要があります.
    まとめ
    実際の生産環境では、プログラムのコンパイルと実行は往々にして別々であるが、この一連の過程の原理を明らかにすれば、ダイナミックライブラリのバージョンにめまいを恐れない.簡単に言えば、次のようにします.
  • ダイナミックライブラリのコンパイル時に-Wl,-soname,libxxx.so.aを指定し、sonameをlibxxxとする.so.a,実際の動的ライブラリファイルlibxxxを生成する.so.a.b.c,
  • 実行可能プログラムのコンパイル時にlibxxを保証する.soが存在する場合、ソフトチェーンであれば、実際のダイナミックライブラリファイルlibxxxを指す必要がある.so.a.b.c
  • 実行可能ファイルの実行時にlibxxxを保証する.so.a.b.cファイルが存在し、ldconfigによりlibxxxを生成する.so.aリンクはlibxxxを指す.so.a.b.c
  • 環境変数LD_を設定するLIBRARY_PATH、実行可能プログラム
  • を実行する
    はい、以上はこの文章のすべての内容で、本文の内容がみんなの学习あるいは仕事に対して一定の助けをもたらすことができることを望んで、もし疑问があればみんなは伝言を残して交流することができます.