ttt

getttyent

(FreeBSD) portsでビルド中に、undefined reference to `__mulxc3@GCC_4.0.0' というエラー

2007-08-31 23:28:40 | デジタル・インターネット

FreeBSD 6.2-STABLEなマシンで、portsを使って、とあるソフトをビルド中、こんなエラーメッセージが出て終了しました。

/usr/local/lib/gcc-4.2.2/libgfortran.so.2: undefined reference to `__mulxc3@GCC_4.0.0'
/usr/local/lib/gcc-4.2.2/libgfortran.so.2: undefined reference to `__mulsc3@GCC_4.0.0'
/usr/local/lib/gcc-4.2.2/libgfortran.so.2: undefined reference to `__divsc3@GCC_4.0.0'
/usr/local/lib/gcc-4.2.2/libgfortran.so.2: undefined reference to `__muldc3@GCC_4.0.0'
/usr/local/lib/gcc-4.2.2/libgfortran.so.2: undefined reference to `__divdc3@GCC_4.0.0'
/usr/local/lib/gcc-4.2.2/libgfortran.so.2: undefined reference to `__divxc3@GCC_4.0.0'
*** Error code 1

とりあえず、エラーの原因に関係しそうなlibgfortran.so.2をlddで調べてみると、こんな感じで、ちょっと変。

# ldd /usr/local/lib/gcc-4.2.2/libgfortran.so.2
/usr/local/lib/gcc-4.2.2/libgfortran.so.2:
        libm.so.4 => /lib/libm.so.4 (0x281fd000)
        libgcc_s.so.1 => /usr/local/lib/gcc/i386-portbld-freebsd6.2/3.4.6/libgcc_s.so.1 (0x28213000) なんか変だぞ?!
        libc.so.6 => /lib/libc.so.6 (0x2807d000)

gcc-4.2.2のlibgfortran.so.2なのに、gcc-3.4.6のライブラリをリンクするなんて、ありえない感じです。

試しに、他のFreeBSD 6.2-STABLEなホストで確認してみると、こんな風になって、ちゃんとgcc-4.2.2のほうがリンクされてます。

# ldd /usr/local/lib/gcc-4.2.2/libgfortran.so.2
/usr/local/lib/gcc-4.2.2/libgfortran.so.2:
        libm.so.4 => /lib/libm.so.4 (0x881fd000)
        libgcc_s.so.1 => /usr/local/lib/gcc-4.2.2/libgcc_s.so.1 (0x88213000)
        libc.so.6 => /lib/libc.so.6 (0x88086000)

たぶん、こうなるのが正しい姿。

pkg_whichで、ちょっと確認。

# pkg_which /usr/local/lib/gcc/i386-portbld-freebsd6.2/3.4.6/libgcc_s.so.1
[Updating the pkgdb <format:bdb_btree> in /var/db/pkg ... - 1043 packages found (-0 +0)  done]
gcc-3.4.6_1,1

まあ、当然の結果ですね。

ここで、ふと気がつきました。

まてよ、OS標準のGCCって、バージョンはいくつだ?!

# which gcc
/usr/bin/gcc

# gcc -v
Using built-in specs.
Configured with: FreeBSD/i386 system compiler
Thread model: posix
gcc version 3.4.6 [FreeBSD] 20060305

/usr/bin/gccは、gcc-3.4.6だ!

で、portsでインストールされたgccは?

# which gcc34
/usr/local/bin/gcc34

# gcc34 -v
Reading specs from /usr/local/lib/gcc/i386-portbld-freebsd6.2/3.4.6/gcc/i386-portbld-freebsd6.2/3.4.6/specs
Configured with: ./..//gcc-3.4.6/configure --disable-nls --with-system-zlib --with-libiconv-prefix=/usr/local --program-suffix=34 --libdir=/usr/local/lib/gcc/i386-portbld-freebsd6.2/3.4.6 --with-gxx-include-dir=/usr/local/lib/gcc/i386-portbld-freebsd6.2/3.4.6/include/c++/ --prefix=/usr/local --mandir=/usr/local/man --infodir=/usr/local/info/gcc34 i386-portbld-freebsd6.2
Thread model: posix
gcc version 3.4.6 [FreeBSD]

これも、同じ、gcc-3.4.6だ!

OS標準のコンパイラとして、もともとgcc-3.4.6が入ってるのに、なぜportsでも同じバージョンのgccがインストールされているのだ!?

いったいだれがgcc-3.4.6をインストールさせたのか? /var/db/pkg/gcc-3.4.6_1,1/+REQUIRED_BY を見てみると、たとえばliboilなどが含まれていました

liboilのMakefileを見てみると、こんなのが書かれています。

.if ${OSVERSION} < 600000 && ${OSVERSION} > 500000 && !defined(WITH_3DNOW_GCC40)
BUILD_DEPENDS+= gcc34:${PORTSDIR}/lang/gcc34
RUN_DEPENDS+=   gcc34:${PORTSDIR}/lang/gcc34
CC:=    gcc34
CXX:=   g++34
.endif

FreeBSD 5系のマシンで、liboilなどをインストールしたときに、依存関係でgcc-3.4.6がインストールされるようです。

なるほど、判明しました。

実は、このビルドでエラーが起きてたホストは、つい最近、FreeBSD 5-STABLEから、make buildworld、make installworldなどの手順を経て、FreeBSD 6へアップグレードしたのでした。

だから、

  1. FreeBSD5だったころに、portsでgcc34がインストールされていた。
  2. FreeBSD 6へアップグレードした。
  3. portupgradeしまくった。
  4. もともと入ってたgcc34も、一応、portupgradeされた。
  5. gcc34が入ってたがために、ldconfigで、/usr/local/lib/gcc/i386-portbld-freebsd6.2/3.4.6/が、/usr/local/lib/gcc-4.2.2/よりも先に参照されるように、パスが設定されてしまっていた。

というわけで、

# ldd /usr/local/lib/gcc-4.2.2/libgfortran.so.2
/usr/local/lib/gcc-4.2.2/libgfortran.so.2:
        libm.so.4 => /lib/libm.so.4 (0x281fd000)
        libgcc_s.so.1 => /usr/local/lib/gcc/i386-portbld-freebsd6.2/3.4.6/libgcc_s.so.1 (0x28213000)
        libc.so.6 => /lib/libc.so.6 (0x2807d000)

になってしまうのか。

ということは、portsでインストールしたgcc34を、アンインストールしてしまえばよさそうです。

ただ、gcc34に依存しているパッケージがあったので、まずは、/var/db/pkg/gcc-3.4.6_1,1/+REQUIRED_BY に入っているものすべてportupgrade -fで、再インストールしてみました。

すると、/var/db/pkg/gcc-3.4.6_1,1/+REQUIRED_BY が空っぽになりましたので、安心して、アンインストール。

# pkg_delete gcc-3.4.6_1,1

lddで確認。

# ldd /usr/local/lib/gcc-4.2.2/libgfortran.so.2
/usr/local/lib/gcc-4.2.2/libgfortran.so.2:
        libm.so.4 => /lib/libm.so.4 (0x281fd000)
        libgcc_s.so.1 => /usr/local/lib/gcc-4.2.2/libgcc_s.so.1 (0x28213000)
        libc.so.6 => /lib/libc.so.6 (0x2807d000)

正しそうな状況になっています。

ちなみに最初に、ldconfig -rで確認してれば、どの順番で、共有ライブラリが探されるのかがわかりますね。

/var/run/ld-elf.so.hints:
        search directories: /lib:/usr/lib:/usr/lib/compat:/usr/local/lib:/usr/local/lib/mysql:以下省略

今回はpkg_deleteしてしまうことで解決しましたが、別に、FreeBSD6のマシンにportsでgcc34をインストールして悪いこともないと思います。

ldconfigでのライブラリの検索パスしだいで、おかしな状況になってしまうのが、本当の原因なんじゃないでしょうか? 今回の場合は、libgfortran.so.2が、ライブラリを変なディレクトリからひっぱりこんだのが悪い。

えーと、ldconfigや、LD_LIBRARY_PATHなんかに影響されずに、パスをきめうちでライブラリをリンクする方法があったような気がするんですが、ま、いっか。

なんか重大発表が!?
正直、ちょっとさびしいかも。