忍者ブログ

素人翻訳

適当に翻訳する。

下潜結合(underlinking)

出典:Underlinking
許諾:Creative Common - Attibution - ShareAlike 3.0.

下潜結合(Underlinking)
目次
下潜結合とは何か
単純な例

バイナリが使用しているシンボルに、直接結合されたライブラリによっては提供されないものが含まれている時、その状態を下潜結合という。例えば、libeb では zlib の「deflate」が使われている。しかし libeb の構築にあたっては -lz は付けられていない。このやり方は共有ライブラリでは認められている。

objdump -p /usr/lib/libeb.so | grep NEEDED
   NEEDED   libnsl.so.1
   NEEDED   libresolv.so.2
   NEEDED   libc.so.6

libeb を利用するプログラムには、明示的に libz を結合する必要がある。直接 libz を使っていなくても。

% echo 'main() { eb_log("foo"); }' > t.c
% gcc t.c -leb -lz
% gcc t.c -leb
/usr/lib/libeb.so: undefined reference to `inflateEnd'
/usr/lib/libeb.so: undefined reference to `deflate'
...
collect2: ld returned 1 exit status

上のプログラムは -Wl,--as-needed を用いて結合してはいけない。(過剰結合)

% gcc t.c -Wl,--as-needed -leb -lz
/usr/lib/libeb.so: undefined reference to `inflateEnd'
/usr/lib/libeb.so: undefined reference to `deflate'
...
collect2: ld returned 1 exit status
表に現れない例

例えば、librsvg は libm の「tan」を使用しているが、構築時に「-lm」は付けられていない。

この librsvg と libm を基にした下潜結合は、先の単純な例とは異なり、librsvg を伴って構築されたプログラムに何の影響も及ぼさない。これは librsvg が多くの共有ライブラリと結合されており、その共有ライブラリの中に libm と結合されているものが存在することに因る。

なぜ下潜結合は有害なのか
プログラムに有害

新しい版の libfoo が公開され、今度の libfoo は zlib を使用している、しかし libfoo 構築時に zlib は結合していない、この場合どうなるか。libfoo を使用している全てのプログラムは「-lz」を付けて構築し直すことになる。

--as-needed が使えない

上で述べたように、下潜結合しているライブラリを付してプログラムを構築する時、--as-needed を使ってはいけない。

例:

% gcc -Wl,--as-needed conftest.c -lgtk -lgdk -lgmodule -lgthread -lglib -lpthread -ldl -lXi -lXext -lX11 -lm
/usr/lib/libgtk.so: undefined reference to `gdk_window_foreign_new'
/usr/lib/libgtk.so: undefined reference to `gdk_drag_find_window'
/usr/lib/libgtk.so: undefined reference to `gdk_colormap_get_visual'
...

「-lgdk」が挙げられているが、conftest.c はこれを使用していないので、--as-needed 指定によって捨てられる。

解決方法は /usr/lib/libgtk.so を適切に libgdk と結合すること。未定義の参照が libgtk.so に含まれないようにする。

配布に不便

配布に際しては、パッケージの間の依存関係を知る必要がある。下潜結合状態のライブラリがあると、再構築するべきか判断できなくなる。(BuildRequires を確認すれば話は別だが。) また、これによって過剰結合せざるを得なくなる。

例:

libeb を必要とするプログラム foo は libeb と libz を付けて構築される。もし ABI の異なる新しい libz が公開されたら:

  • foo を無駄に再構築しなければならない。(過剰結合)
  • libeb も再構築する必要がある。しかしこの情報は隠されている。ライブラリの依存関係を自動で処理する機能は役に立たない。
検出方法
パッケージ構築に際して
ld の --no-undefined 指定

ROSA Linux では「-Wl,--no-undefined」が標準で LDFLAGS に設定されている。これによって全ての共有ライブラリ(DSO)が下潜結合されないようになる。足りないライブラリがあると構築に失敗する。例:

% echo 'main() { deflate(); }' > libfoo.c
% gcc -shared libfoo.c
% gcc -shared libfoo.c -Wl,--no-undefined
ccEqBIQo.o: In function `main':
libfoo.c:(.text+0x12): undefined reference to `deflate'
collect2: ld returned 1 exit status

--no-undefined を標準で用いると問題が起きることもある。特に plugins/modules に。下記を見よ

spec-helper の警告

ここで説明されている警告を見ることがあるだろう。(spec-helper の strip_and_check_elf_files によって調べられる。)

しかしこの検査は ldd -r を使っており、検出できない場合もある。

共有ライブラリを調べる

「ldd -r」を使う。例:

% ldd -r /usr/lib/libeb.so.10.0.2 >/dev/null
undefined symbol: inflateEnd   (/usr/lib/libeb.so.10.0.2)
undefined symbol: deflate   (/usr/lib/libeb.so.10.0.2)
undefined symbol: deflateInit_   (/usr/lib/libeb.so.10.0.2)
undefined symbol: inflate   (/usr/lib/libeb.so.10.0.2)
undefined symbol: deflateEnd   (/usr/lib/libeb.so.10.0.2)
undefined symbol: inflateInit_   (/usr/lib/libeb.so.10.0.2)

「ldd -r」では十分でない。「表に現れない場合」を検出できない。構築時に --no-unneeded を使えばあらゆる場合に対応できる。

修正方法
libtool を使ったプログラム

修正の例:

distcache
--- distcache-1.5.1/ssl/libnalssl/Makefile.am 2004-04-30 13:09:14.000000000 -0400
+++ distcache-1.5.1.oden/ssl/libnalssl/Makefile.am 2008-05-21 12:11:36.000000000 -0400
@@ -3,3 +3,4 @@
lib_LTLIBRARIES = libnalssl.la
libnalssl_la_SOURCES = bss_nal.c
libnalssl_la_LDFLAGS = -version-info 1:1:0
+libnalssl_la_LIBADD = ../../libnal/libnal.la
gtk+1.2
--- gtk+-1.2.10/gtk/Makefile.am.gtkgdkdep 2003-10-15 15:20:27.000000000 -0400
+++ gtk+-1.2.10/gtk/Makefile.am 2003-10-15 15:22:50.000000000 -0400
@@ -23,6 +23,10 @@

# libtool stuff: set version and export symbols for resolving
libgtkincludedir = $(includedir)/gtk-1.2/gtk
+
+libgtk_la_DEPENDENCIES = $(top_builddir)/gdk/libgdk.la
+libgtk_la_LIBADD = $(top_builddir)/gdk/libgdk.la
+
libgtk_la_LDFLAGS = @STRIP_BEGIN@ \
-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \
-release $(LT_RELEASE) \
id3lib

make libid3_la_LIBADD="-lstdc++ -lz" で修正できる。

二つの問題に気をつける。

  • -lz は付けられていない。
  • libtool --mode=link g++ は正しくない。なぜなら libtool は共有ライブラリとの結合に g++ ではなく gcc を用いるから。正しい命令は:
    libtool --tag=CXX --mode=link g++。残念なことに --tag=CXX は libtool 1.4b(2001年7月)から導入された機能であり、id3lib の libtool では扱えない。
--no-undefined で生じる問題

mpg123 を例に採る。プラグインは呼出し元のプログラムで定義された関数を使用している。

% ldd -r /usr/lib/mpg123/output_sdl.so >/dev/null
undefined symbol: sfifo_close (/usr/lib/mpg123/output_sdl.so)
undefined symbol: sfifo_init (/usr/lib/mpg123/output_sdl.so)
undefined symbol: sfifo_flush (/usr/lib/mpg123/output_sdl.so)
undefined symbol: sfifo_read (/usr/lib/mpg123/output_sdl.so)
undefined symbol: sfifo_write (/usr/lib/mpg123/output_sdl.so)
% objdump -T /usr/bin/mpg123 | grep sfifo
0805ac30 g DF .text 00000013 Base sfifo_flush
0805ac80 g DF .text 0000007a Base sfifo_init
0805adc0 g DF .text 000000b8 Base sfifo_read
0805ad00 g DF .text 000000c0 Base sfifo_write
0805ac50 g DF .text 00000021 Base sfifo_close

ここに挙げられたシンボル(プラグインの中では解決されない)は、そのプラグインを呼び出したプログラムの一部である。(以下のプログラムのプラグインが例。gnome-settings-daemon、compiz*、emerald、libxslt、lxpanel など。)

これらの特別なプログラムで下潜結合を禁じるには、次の一文を仕様文書に加える。

%define _disable_ld_no_undefined 1

この文が必要ないことも多々ある。というのは、%configure で呼び出される drop-ld-no-undefined-for-shared-lib-modules-in-libtool によって ltmain.sh/libtool が修正され、共有ライブラリ構築時に --no-undefined が無視されるようになるから。

より良い修正方法は libtool が -module 指定付きで正しく呼ばれるようにすること。(時々 -export-dynamic しか使われないことがある。両方用いられるべきなのに。)

Perl 機能群

ROSA Linux は Perl 機能群に対して --no-undefined 指定をしていない。Perl 機能群は構築の際、LDFLAGS を利用しないから。

Perl 機能群は libperl と結合されないので、むしろ指定するべきでない。

Ruby 機能群

Ruby 機能群は libruby に対して結合されるので --no-undefined は必要ない。ruby/GNOME2 が壊れてしまうので、機能群構築にあたって --no-undefined は元々使われていない。このパッケージは複数の機能群(glib、gtk、atk、poppler など)を構築するが、これらは結合されない。そのかわりに、ruby コードによって然るべき順序で読込まれる。この特別なパッケージを修正するのが筋だが、今のところ ruby パッケージの方に解決処理が入っている。

PR

過剰結合(overlinking)

出典:Overlinking
許諾:Creative Common - Attibution - ShareAlike 3.0.

過剰結合(Overlinking)
目次
過剰結合とは何か

例:libpng 機能を要する emacs の構築にあたって、静的 PNG ライブラリを結合する場合、「-lz」を使う。ところが、動的結合だとこれでは拙い。zlib の版の上位番号が更新された時、emacs が zlib を全く使っていなくても libpng がそれを使用しているというだけで、emacs を再構築しなければならない。

検出方法
バイナリを調べる

「ldd -u -r」を使う。以下が例。

% ldd -u -r /usr/bin/emacs
Unused direct dependencies:

   /usr/lib/libXext.so.6
   /lib/libz.so.1
パッケージ構築に際して

ここで説明されている警告を見ることになる。(調査は spec-helper の strip_and_check_elf_files によって行われる。)

修正方法
pkg-config の問題

必要な結合印(linker flags)を得るために $(pkg-config --libs somefoo) を使用しているソフトウェアがある。pkg-config は「*.pc」文書の「Libs:」欄と「Requires:」欄を通じて結合印(linker flags)を得る。

しかしながら、多くの「*.pc」文書ではそれ自身の依存関係が誤って「Libs:」や「Requires:」に記述されている。当のパッケージだけが利用し、そのパッケージを用いる他のパッケージには知らせない類いの依存性は、「Libs.private:」と「Requires.private:」に記述されるべきであるにもかかわらず。(これには歴史的な理由がある。Requires.private と Libs.private は 0.18 版以前の pkg-config には存在せず、両者とも後方互換でないのだ。)

gtk+-2.0.pc からの例:

Requires: gdk-${target}-2.0 atk cairo

これは次のように記述するべき:

Requires.private: gdk-${target}-2.0 atk cairo

ffmpeg の libavcodec.pc からの例:

Libs: -L${libdir} -lavcodec -lz -pthread -lm -la52 -lfaac -lfaad -lmp3lame -lm -lnut -ltheora -logg -lvorbisenc -lvorbis -logg -lx264 -lm -lxvidcore -ldl -ldl -lX11 -lXext

これも次のように記述されるべき:

Libs.private: -lz -pthread -lm -la52 -lfaac -lfaad -lmp3lame -lm -lnut -ltheora -logg -lvorbisenc -lvorbis -logg -lx264 -lm -lxvidcore -ldl -ldl -lX11 -lXext
Libs: -L${libdir} -lavcodec
libtool の問題

libtool アーカイヴ(*.la)の中には使用されているライブラリの一覧表があり、ライブラリは再起的に記録されている。標準の libtool はこれらのライブラリを、動的に結合されるものも含めて、明示的に記述する。この点は debian からのパッチ(patch)で修正されている。

しかしながら、ソフトウェアには大抵それ自身の libtool が同梱されている。configure の前に「autoreconf」を走らせ、これを更新する方法もある。しかしこのやり方は失敗することもよくあるので、Mandriva は %configure と %configure2_5x で呼び出される「fix-libtool-overlinking」という指示文(script)を用意している。

libtool archives も見よ。

色々な問題

残念なことに、libtool と *.pc 文書を修正するだけでは十分ではない。

  • xdm は -lXinerama を附けて構築されているが、本当は要らない。/usr/lib/X11/xdm/authdir だけが -lXinerama を必要としている。しかし Makefile を簡単にするために、同じ LDFLAGS が使われている。
  • emacs は静的構築・動的構築の両方で -lz を附けて構築されているが、これは誤り。emacs の configure.in は修正されるべき。

コードを直し、上流の開発者に報告するのが、上記の問題の最良の修正方法だ。

--as-needed

正確に修正して上流の開発者に報告するのは骨が折れるし時間もかかる。--as-needed を使うのが簡単な直し方だ。Mandriva では2008年5月から広域の(global) --as-needed が使われている。

--as-needed で生じる問題
下潜結合のライブラリ(underlinked)

--as-needed によって問題が生じた場合、下潜結合(underlinked)のライブラリが原因かもしれない。

[me@mandrivapc]$ %define _disable_ld_as_needed 1 を使って問題を回避してはいけない。これはその場しのぎにしかならない。ライブラリ自体を修正するべき。(参考:fedora は prelink を導入した2003年には、もうこれらの問題を修正するパッチ(patches)を出していた。)

誤った結合順序
gcc -Wl,--as-needed -lfoo bar.o

これは良くない。「ld」は後ろに続く文書群の中から未解決のシンボルを探すから。そのため [me@mandrivapc]$ -lfoo は --as-needed を使用した場合、単に無視される。

ライブラリが *.o 文書の後に置かれるようにする。

gcc -Wl,--as-needed bar.o -lfoo

順序の誤りは通常、ライブラリを LDFLAGS に食わせた時に起きる。(このやり方は正しくない)

詳しい説明は Gentoo の --as-needed に関する素晴らしいページを見てほしい。

同じシンボルが異なるライブラリに

pcre と --as-needed の使用によって起こる領域違反(segfault) を見てほしい。

参考

カレンダー

12 2025/01 02
S M T W T F S
1 2 4
5 6 7 8 9 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

最新コメント

[09/07 NONAME]
[08/18 NONAME]
[05/18 NONAME]
[04/09 NONAME]
[03/21 NONAME]

最新記事

(01/10)
(01/03)
(12/20)
(12/08)
(11/20)
(10/30)
(10/24)
(09/20)
(09/16)
(09/11)
(09/03)
(09/02)
(08/27)
(08/17)
(07/31)
(07/30)
(07/19)
(07/13)
(05/02)
(03/17)
(11/01)
(07/20)
(05/17)
(04/20)
(03/10)

ブログ内検索

広告

バーコード

広告