- PR -

Red Hat における libc の住み分け

1
投稿者投稿内容
あんとれ
ぬし
会議室デビュー日: 2004/01/14
投稿数: 556
投稿日時: 2007-12-10 13:00
FC の最新バージョンはよく知りませんが、以前の FC や商用版 Red Hat EL では、/lib ディレクトリの中を参照すると以下のような3つの libc がインストールされていて、LD_ASSUME_KERNEL の値によって振り分けを行っているように見えます。

/lib/libc-2.x.y.so
/lib/i686/libc-2.x.y.so
/lib/tls/libc-2.x.y.so

$ uname -r
2.4.21-50.EL
$ ldd a.out
libc.so.6 => /lib/tls/libc.so.6 (0x00424000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x00924000)
$ export LD_ASSUME_KERNEL=2.4.19
[staff@ndspc2421]$ ldd a.out
libc.so.6 => /lib/i686/libc.so.6 (0x00a1b000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x00702000)
$ export LD_ASSUME_KERNEL=2.4.0
$ ldd a.out
libc.so.6 => /lib/libc.so.6 (0x0012c000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x0089e000)

もし、この振り分けをどこで制御しているのか分かる方がいらっしゃれば教えていただけますでしょうか。

恐らく glibc のどこかであろうというところまでは特定できているのですが、よく分かりません。(ソースコード内から i686、tls という文字列を見つけ出すことができません。かといって、/etc/ld.so.conf の中にもそのような設定が見つかりません)

angel
ぬし
会議室デビュー日: 2005/03/17
投稿数: 711
投稿日時: 2007-12-11 01:00
こんばんは。
引用:
あんとれさんの書き込み (2007-12-10 13:00) より:
恐らく glibc のどこかであろうというところまでは特定できているのですが、よく分かりません。(ソースコード内から i686、tls という文字列を見つけ出すことができません。かといって、/etc/ld.so.conf の中にもそのような設定が見つかりません)


ソースコード中にハードコードされているものではないと思います。
素直に考えると、直接の制御はダイナミックローダ、ld-linux.so でしょう。
ソースコード中に、環境変数の値を読み取って、OSバージョンを表す変数を設定しているようなところが見て取れます。

実際にどのライブラリを使うかは、LD_ASSUME_KERNELの説明を見ると、ABIのバージョンにより切り替えをしているようです。
/etc/ld.so.cache でライブラリの情報を持っているので、その情報を基に、ABIバージョンに対応したパスを引っ張ってきているのでしょう。
まとめると、
・ldconfig が /lib や /lib/tls を探索してライブラリ情報を収集
・LD_ASSUME_KERNEL の値により、ld-linux.so がABIバージョンを決定
・収集済みライブラリ情報(ld.so.cache)より、対応するABIバージョンのライブラリのパスをld-linux.soが決定
という流れではないかと推測します。
angel
ぬし
会議室デビュー日: 2005/03/17
投稿数: 711
投稿日時: 2007-12-11 01:12
補足です。

/lib や /lib/tls や /lib/i686 というディレクトリについては、ld-linux.so や ldconfig のライブラリ探索ルールによるものだと思います。

/lib はデフォルトとして、/etc/ld.so.conf に登録されているディレクトリ、プラットフォーム名に対応したサブディレクトリ ( i386 や i686等 )、および TLS対応の場合は tlsサブディレクトリも探索する、ということでしょう。
※tlsサブディレクトリについては、ソースコード中にハードコーディングされているように見えます。プラットフォーム名に対応した名前は、_dl_string_platform 関数で引いてきているのではないでしょうか。
あんとれ
ぬし
会議室デビュー日: 2004/01/14
投稿数: 556
投稿日時: 2007-12-11 19:19
angelさん、ありがとうございます。
教えていただいた内容をベースに簡単なテストをしてみました。(自己レス)

英語の意味がよくわからなかったのですが、結論からいうとライブラリの PT_NOTE セクションを参照しているようでした。

<< abi-note.s >>
コード:
        .section ".note.ABI-tag", "a"
        .p2align 2
        .long 1f - 0f
        .long 3f - 2f
        .long 1
0: .asciz "GNU"
1: .p2align 2
2: .long 0
        .long 2,2,5  <== ★
3: .p2align 2



★のところの値をいろいろと変えたライブラリを3つ作成。

$ gcc -c -Wa,--noexecstack -o abi-note.o abi-note.s
$ gcc -shared -o libtest.so libtest.o abi-note.o

2.4.5 に設定したものを /lib/tls/libtest.so として配置。
2.3.5 に設定したものを /lib/i686/libtest.so として配置。
2.2.5 に設定したものを /lib/libtest.so として配置。

実行ファイルを作成

$ gcc test.c -ltest

LD_ASSUME_KERNEL の値をいろいろと変えてテスト。
ldd を使用してリンクされたライブラリを確認。

2.4.5 以上の場合 : /lib/tls/libtest.so
2.3.5 以上の場合 : /lib/i686/libtest.so
2.2.5 以上の場合 : /lib/libbtest.so

さらに、ライブラリの置き場所を変えて実行ファイル作成時に DT_RPATH を設定してみました。

2.4.5 に設定したものを /usr/local/lib/tls/libtest.so として配置。
2.3.5 に設定したものを /usr/local/lib/i686/libtest.so として配置。
2.2.5 に設定したものを /usr/local/lib/libtest.so として配置。

実行ファイルを作成

$ gcc test.c -ltest -Wl,-rpath -Wl,/usr/local/lib

LD_ASSUME_KERNEL の値をいろいろと変えてテスト。
ldd を使用してリンクされたライブラリを確認。

2.4.5 以上の場合 : /usr/local/lib/tls/libtest.so
2.3.5 以上の場合 : /usr/local/lib/i686/libtest.so
2.2.5 以上の場合 : /usr/local/lib/libtest.so

==> 置き場所に関係なく tls や platform のサブ・ディレクトリがあればそちらのライブラリを優先的に参照しているようです。

ただ、未だに $(libdir)/tls、$(libdir)/i686、$(libdir) の順、デフォルトの検索順が /lib、/usr/lib の順になるように設定している箇所は確認できていません。
angel
ぬし
会議室デビュー日: 2005/03/17
投稿数: 711
投稿日時: 2007-12-15 17:17
こんにちは。
引用:
あんとれさんの書き込み (2007-12-11 19:19) より:
==> 置き場所に関係なく tls や platform のサブ・ディレクトリがあればそちらのライブラリを優先的に参照しているようです。

ただ、未だに $(libdir)/tls、$(libdir)/i686、$(libdir) の順、デフォルトの検索順が /lib、/usr/lib の順になるように設定している箇所は確認できていません。


気になって ldconfig のソース ( elf/ldconfig.c ) を調べていたのですが、おそらく以下のようなルールなのだろうと思います。

1. tls や i686 等のサブディレクトリを除いては、ディレクトリの探索順位がそのままライブラリの優先順になる
 探索順位:/etc/ld.so.conf の先頭に現れる方、/lib、/usr/lib の順
 これは、main() の中で、parse_conf() した後に、add_system_dir(SLIBDIR)、add_system_dir(LIBDIR) している順番と一致します。
 なお、/lib, /usr/lib が SLIBDIR、LIBDIR に対応するわけですが、これは makefile の中でマクロとして設定される値なので、ソースだけを見ても出てきません。

2. tls や i686 等のサブディレクトリは、1. のディレクトリ全てを探索した後に探索され、探索順は 1. に一致する。同一ディレクトリ内にあるサブディレクトリに関しては、探索順はおそらく不定。
 これは、search_dir() で、is_hwcap_platform() が真となるサブディレクトリ ( つまり tls や i686 ) が見つかった場合、探索対象ディレクトリリストに追加が行われる ( 後の search_dir() で処理される ) ことから分かります。

3. tls や i686 等のサブディレクトリは、1. のディレクトリより無条件に優先され、tls の方が i686 より優先、tls同士や i686同士は、親ディレクトリの 1. での優先順位に従う。
 search_dir() の先頭で、path_hwcap() を使ってディレクトリ毎のハードウェアマスクの値を取得しているのですが、これがそのまま優先順位に結びつくのだろうと推測されます。( ldconfig -v で見られる hwcap という値です )
tls同士や、i686同士に関しては、1 と同じように探索順と考えられます。( 探索順は 2 の通り )

実験を行った限りでは、上のルールで合っていると思います。
※/lib/libc.so.6 を色々なディレクトリにコピーして、ldconfig した後に、LD_ASSUME_KERNEL=2.4.0 /bin/cat して、どこのライブラリがロードされるかを見る、という実験
※本当は、elf/dl-load.c を見て、LD_LIBRARY_PATH が設定されている時の探索動作も調べるべきなのでしょうが…、そこまでのパワーはありませんでした。

[ メッセージ編集済み 編集者: angel 編集日時 2007-12-15 17:33 ]
あんとれ
ぬし
会議室デビュー日: 2004/01/14
投稿数: 556
投稿日時: 2007-12-17 19:00
何度もありがとうございます。

自分も実際にソースを追ってみましたが、確かにそうなってますね。

SLIBDIR は prefix=/usr と指定された場合のみ /lib もしくは /lib64、それ以外の場合は LIBDIR に設定されているようです。

elf/cache.c を見る限りでは、ld.so.cache に格納する前に hwcap の大きいものが先になるように入れ替えているようです。

あと、見つかったサブ・ディレクトリはリストの末尾に追加されるので、結果的にはサブ・ディレクトリの探索は再帰的に行われているようです。なので、/lib/tls/tls/tls/tls というディレクトリがあった場合、このディレクトリも探索の対象に入るようです。もちろん、これが作者の意図する動作とは思えないですが・・・。(hwcap の値がオーバーフローしてしまいました)

引用:

※本当は、elf/dl-load.c を見て、LD_LIBRARY_PATH が設定されている時の探索動作も調べるべきなのでしょうが…、そこまでのパワーはありませんでした。



恐らくですが _dl_map_object あたりで行っているのでしょうか。infoファイルに記述されているとおり、こんな順で探索しているようです。

1. DT_RUNPATH が設定されていないときに RPATH
2. LD_LIBRARY_PATH
3. DT_RUNPATH が設定されていれば DT_RUNPATH
4. ld.so.cache にキャッシュされている内容
5. /lib, /usr/lib

サブ・ディレクトリの検索ルーチンについてはよく分かりませんでしたが、elf/dl-load.c の open_path 関数の中で _dl_important_hwcaps 関数の戻り値 catstr を参照しているようなので、この辺りでやっているのでしょうか。

あと、/lib, /usr/lib の値については、やはりマクロ定義されていて (SYSTEM_DIRS)、元へ辿って行くと、Makerules の中でこんな風に設定されていました。(実際には elf/gen-trusted-dirs.awk で trusted-dirs.h を生成して include されるようになっているようです)

コード:
ifneq ($(libdir),$(slibdir))
default-rpath = $(slibdir):$(libdir)
else
default-rpath = $(libdir)
endif



ちなみに、ダイナミック・ローダーは /lib/tls/tls/tls/tls というディレクトリにライブラリがあっても見つけられないようです。

1

スキルアップ/キャリアアップ(JOB@IT)