はじめに
Steam で Linux に対応したゲームを配信しているのですが、ユーザーさんから 「glibc のバージョンを下げてくれないか?」 と依頼されました。
Linux 版のゲームを自分の PC (Ubuntu 22.04.4 LTS) でローカルビルドしたものをリリースしていたのですが、その環境の glibc は 2.35、実行モジュールの glibc バージョンは 2.34 となっています。
(glibc のバージョン確認)
$ ldd --version
ldd (Ubuntu GLIBC 2.35-0ubuntu3.6) 2.35
Copyright (C) 2022 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.
(ゲームモジュールの依存 glibc バージョンの確認方法)
$ readelf -W -a '~/.local/share/Steam/steamapps/common/Battle Marine/bmarine' | grep libc_start_main
0000000000285fb0 0000002100000006 R_X86_64_GLOB_DAT 0000000000000000 __libc_start_main@GLIBC_2.34 + 0
33: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.34 (4)
1082: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.34
ビルド環境の Linux はもっと低いバージョンの Linux にする必要がありそうです。
コンテナでビルド環境を構築
もちろん、自分の PC の Linux バージョンは下げたくないので、ビルド用に古い Linux のコンテナを作ります。
コンテナといえば Docker がよく使われるかもしれませんが、今回のケースなら lxd の方が軽くて良い感じでしたので、私が実施した構築手順を共有します。
lxd を準備
sudo snap install lxd
sudo lxd init
lxd init の設定項目は全部デフォルト(enter)
コンテナ作成
lxc launch ubuntu:18.04 steam-build
当初、なるべく古いバージョンが良いかと思い ubuntu は 16.04 をインストールしてみたのですが、ネットワークが繋がらないなど色々と不便だったので 18.04 を使うようにしました。(多分、古いバージョンを使うには色々と面倒な設定が必要なはず)
コンテナへのログイン
lxc exec steam-build bash
コンテナにビルド環境を構築
sudo apt update
sudo apt install build-essential
あとは github の環境設定なりをしてビルドするだけです。
なお、コンテナから外部ホストへの通信がタイムアウトする場合は iptables の変更などが必要になるかもしれません。
この環境でビルドしたモジュールの GLIBC バージョンは 2.2.5 になりました。
$ readelf -W -a bmarine | grep start_main
000000000047cfd0 0000005a00000006 R_X86_64_GLOB_DAT 0000000000000000 __libc_start_main@GLIBC_2.2.5 + 0
90: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (3)
2567: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_2.2.5
コンテナの起動・停止・削除
# 起動
lxc start steam-build
# 停止
lxc stop steam-build
# インスタンス削除
lxc delete steam-build
もっと美しい対処
今回、GLIBC のバージョンを下げる対応をしましたが、GLIBC のバージョンを下げなくてもコンパイルとリンク時に次のオプションを指定することで GLIBC バージョン依存の問題を解消できるようです。
-static-libgcc -static-libstdc++ -ftls-model=global-dynamic
- -static-libgcc will dodge libgcc ABI issues (which are common)
- -static-libstdc++ will dodge c++ ABI issues (which are legions and happening all the time)
- -ftls-model=global-dynamic will spare ELF static TLS slots for system libs (because they are somewhat rare)
私はビルド環境の CI 化のためにコンテナを用いる方式も別途検討していて、その方式の方が色々とリスクが少ないかなということでコンテナ方式を採用しましたが、こちらの対処方法の方が良いと思います。
今回はこのオプションを付与しつつ古いバージョンのGLIBCを使う安全策を取っておいたので、今後GLIBCのアップデートが必要になっても同じ問題を回避できるかもしれません。