uchan note

プログラミングや電子工作の話題を書きます

GCC 11のビルドとldconfig

GCC 11 をソースコードからビルドする方法と、GCC 11 に付属する標準 C++ ライブラリを利用するための ldconfig コマンドを紹介します。

GCC 11.1.0 のビルド

基本は Build GCC 11 from source on Ubuntu と同じ手順で OK です。 Ubuntu 18.04 と 20.04 でビルドできることを確認しました。

GCC はソースディレクトリでの ./configure に対応していませんので、ソースディレクトリ以外にビルド用ディレクトリを作ります。 この記事では次のようなディレクトリ構成にしていますね。

$HOME/
  gcc-releases-gcc-11.1.0/
    configure
    contrib/download_prerequisites
  build/

私は gcc-releases-gcc-11.1.0/build という感じでビルドディレクトリを作りましたが、上手くビルドできました。

ビルド設定は次の通りとしました。

../configure --prefix=/usr/local/gcc-11.1.0 --enable-languages=c,c++ --disable-multilib --program-suffix=-11.1

--prefix はインストール先ディレクトリを指定します。上記のように指定すると /usr/local/gcc-11.1.0/bin/ 以下にコンパイラgcc や g++ など)が、/usr/local/gcc-11.1.0/lib64/ 以下に C++ 標準ライブラリがインストールされることになります。

--enable-languages で必要な言語を選択します。先の記事では fortran も指定していましたが、私は使わないので。少ないほどビルドが早く終わります。

--disable-multilib を指定しない場合、次のエラーが発生しました。

collect2: error: ld returned 1 exit status

configure: error: I suspect your system does not have 32-bit development libraries (libc and headers). If you have them, rerun configure with --enable-multilib. If you do not have them, and want to build a 64-bit-only compiler, rerun configure with --disable-multilib.

GCC はデフォルトで、32 ビットおよび 64 ビット向けバイナリを生成する機能を持ちます。 しかし、32 ビット向けバイナリを生成する機能を有効にするためには、システムに 32 ビットバージョンのライブラリ(libc など)がインストールされている必要があります。 手元のマシンには 32 ビット版のライブラリ群がインストールされておらず、また、64 ビット向けのビルドだけできれば当面は困らないので、 --disable-multilib を付与することとしました。

--program-suffix=-11.1 は、ビルドされた実行ファイルのサフィックスを指定します。例えば g++-11.1 のような名前になります。

ldconfig

make と make install を実行すると /usr/local/gcc-11.1.0/bin/ 以下にコンパイラがインストールされます。 後はここにパスを通すだけでいいかな、と思うと、実はそれでは不十分というお話です。

結論としては /etc/ld.so.conf.d/ 以下に設定を置いて sudo ldconfig を実行しましょうね、ということです。

共有ライブラリ

g++-11.1 でとあるプログラムをビルドし、実行しようとしたところ、次のようなエラーが表示され、実行できませんでした。

$ ./opelac
./opelac: /lib/x86_64-linux-gnu/libstdc++.so.6: version `GLIBCXX_3.4.29' not found (required by ./opelac)

C++ 標準ライブラリの新しめの機能を使うと表示されるのだと思います。 試しに strings /lib/x86_64-linux-gnu/libstdc++.so.6 | grep GLIBCXX_ としてみると、3.4.28 までしか含まれていないことが分かりました。

実行ファイルが利用する共有ライブラリは ldd で確認できます。

$ ldd opelac
./opelac: /lib/x86_64-linux-gnu/libstdc++.so.6: version `GLIBCXX_3.4.29' not found (required by ./opelac)
        linux-vdso.so.1 (0x00007ffd51fde000)
        libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f0266d6c000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f0266c1d000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f0266c02000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0266a10000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f0266f57000)

確かに opelac(g++-11.1 でビルドしたやつ)が libstdc++.so.6 という名前で /lib/x86_64-linux-gnu/libstdc++.so.6 を参照することが分かります。 新しいコンパイラGCC 11)でビルドした実行ファイルが、実行時にシステム標準の古いライブラリを参照してしまっているわけです。 これがエラーの原因ですね。

先ほどインストールした GCC 11 に含まれる libstdc++ を利用するようにすれば解決します。

やり方はいくつかあります。1 つは LD_LIBRARY_PATH という環境変数にライブラリファイルがあるディレクトリを設定してから opelac を実行することです。

$ LD_LIBRARY_PATH=/usr/local/gcc-11.1.0/lib64 ./opelac

共有ライブラリを一時的に差し替えてプログラムを動作させる場合にとても便利なやり方です。 ただ、毎回 LD_LIBRARY_PATH を指定しなければならず、面倒です。 恒久的に LD_LIBRARY_PATH を設定しておくには $HOME/.bashrc 等に設定を記載してください。 $HOME/.profile では LD_LIBRARY_PATH を設定できませんので注意してください。 参考 LD_LIBRARY_PATH を設定しても反映されないことがある

もう 1 つのやり方は環境変数ではなく、ld の設定ファイルを調整する方法です。 この方法はシステム全体に影響を及ぼすことができます。

$ cat /etc/ld.so.conf.d/gcc-11.1-lib64.conf
/usr/local/gcc-11.1.0/lib64
$ sudo ldconfig

こんな感じで、ライブラリの置き場所を書いたテキストファイルを配置し、ldconfig コマンドを実行するだけです。

この設定を行うと、特にシステム再起動などせず、すぐに参照先ライブラリが切り替わります。ldd で確認してみます。

$ ldd opelac
        linux-vdso.so.1 (0x00007ffffca3b000)
        libstdc++.so.6 => /usr/local/gcc-11.1.0/lib64/libstdc++.so.6 (0x00007efcbd7ce000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007efcbd67f000)
        libgcc_s.so.1 => /usr/local/gcc-11.1.0/lib64/libgcc_s.so.1 (0x00007efcbd664000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007efcbd472000)
        /lib64/ld-linux-x86-64.so.2 (0x00007efcbd9f0000)

libstdc++.so.6 の参照先が変わっていることが分かると思います。 これで、opelac コマンドがエラー無く動作するようになりました。