世界を動かす技術を、日本語で。

Linuxバイナリ互換性の聖杯:MuslとDlopen

2026年1月26日原文(github.com)

概要

  • GoGodot でAndroid/iOS向けのバイナリ生成は容易
  • Linux ではバイナリ互換性と グラフィックス 周りに大きな課題
  • muslglibc の互換性問題が顕著
  • musl 対応のため独自パッチやビルド手法を導入
  • 静的バイナリ+グラフィックス実現には dlopen 問題の克服が鍵

Linuxバイナリ互換性の壁

  • Go の静的バイナリはLinuxサーバーやCLI向けに広く普及
  • グラフィックス 利用時はGPUドライバがC ABI経由で動的ライブラリを要求
  • glibcmusl 間の互換性問題
    • glibc製バイナリはmusl環境で動作不可、逆も同様
  • Void Linux (musl版)での実体験
    • Zed editorやgraphics.gdプロジェクトのビルド困難
  • Goはmusl向け c-shared / c-archive ビルドに未対応

muslサポートのための工夫

  • GOOS=musl という独自ビルドターゲットをgraphics.gdに導入
  • musl向け c-shared ビルドを廃止、 c-archive でGodotと直接リンク
  • これによりmuslサポートを実現
  • Linux向けリリース時は glibc版musl版 の2種類を用意する必要
    • ユーザーに正しいバイナリ選択を促す課題

静的バイナリ+グラフィックスの挑戦

  • muslは 静的リンク に優れるため、単一静的バイナリの実現を模索
  • Godotは依存ライブラリを内包、残りは動的ロード(dlopen)で対応
  • -static 指定でビルドすると「Dynamic loading not supported」エラー
  • muslは静的バイナリでの dlopen 実装を拒否
    • glibcとmuslの TLS 実装差異が原因

dlopen問題の突破口

  • dlopen は弱シンボルとしてコンパイルされるため、自前実装が可能
  • C言語のdetour技法やCosmopolitanのdlopen手法が参考
  • 小型Cプログラムを組み込んで実行時にホストのダイナミックリンカを呼び出し
    • システムのdlopenを「奪い」、graphics.gdに戻る
    • アセンブリトランポリンでlibc TLSを切り替えつつ動的関数をラップ
  • cgoに類似したアプローチ

シングル静的バイナリ+グラフィックスの実現

  • musl+独自dlopen実装で、 Go製シングル静的バイナリ+グラフィックス がLinuxで実現
  • どのLinux(3.2以降)でもハードウェアアクセラレーション付きで動作可能

サンプルとクロスコンパイル手順

  • Dodge The Creeps サンプルプロジェクトの静的バイナリ公開
    • https://release.graphics.gd/dodge_the_creeps.static
  • 任意のプロジェクトをクロスコンパイル可能
    • GOOS=musl GOARCH=amd64 gd build コマンド
    • export_presets.cfgを削除して新しいmuslエクスポートプリセットを追加

今後の展望と課題

  • helperバイナリ の埋め込みや配布方法の改善
  • glibc/musl 混在環境でのユーザー体験向上
  • シングルバイナリ配布の標準化に向けたさらなる工夫

Hackerたちの意見

実行可能ファイルを受け取って、必要な.soファイルを全部集めて、静的実行ファイルか、どこでも動くパッケージを作るツールってある?

必要な.soファイルを一つのファイルに「パッケージ」することはできるよ。そういうツールはたくさんあるし(例えばzipファイルみたいに)。でも、.soファイルを使って一つの「静的」バイナリを作ることはできないんだ。

こういうのはあるよ。思いつくのは次のものかな:1. appimage https://appimage.org/ 2. nix-bundle https://github.com/nix-community/nix-bundle 3. guixのguix pack 4. ほとんど誰も使わないランダムな小プロジェクトの小集まり(例: https://github.com/NilsIrl/dockerc ) 5. dockerイメージ(dockerランタイムがあればどこでも動くパッケージ) 6. https://flatpak.org/ 7. https://en.wikipedia.org/wiki/Snap_(software) AppImageが一番近いと思うよ。

Ermine: https://www.magicermine.com/ 意外とよく動くけど、料金が隠されてて、学生の時に連絡したら年間350ドル以上だったよ。

「実際にポータブルな実行可能ファイル」/cosmopolitan libcというプロジェクトがあるよ。https://github.com/jart/cosmopolitan これは、一度コンパイルすればどこでも実行できるスタイルのC++バイナリを可能にするんだ。

15〜30年前、SolarisとLinuxで動く商業用チップ設計EDAソフトウェアをたくさん管理してたんだ。各プログラムが必要とする特定のライブラリのバージョンを指すために、LD_LIBRARY_PATHやLD_PRELOADを使ったラッパーシェルスクリプトがたくさんあったよ。「ldd」を使って、プログラムが使用する共有ライブラリを表示させてた。

(推奨じゃないけど、知ってる)https://www.magicermine.com/

「これを一つ実行すればパッケージ化できる」っていうほど簡単じゃないと思うから、プロセスが重要ならこれじゃダメかも。でも、ユーザー視点から見るとAppImagesの動きに似てると思う。AppImageは基本的に静的バイナリと、そのアプリケーションの「ルート」を含む小さなファイルシステムイメージがペアになってるって理解してる。フォーマットのすべてが好きなわけじゃないけど、全体的にはflatpakやsnapみたいな他の「ユニバーサル」パッケージよりも、ずっと規定が少ない感じがする。簡単に抽出して再パッケージ化したい部分を選べるのも(バイナリには --appimage-extract みたいなフラグがあるから)助かるよね。

mkdir chroot cd chroot for lib in $(ldd ${executable} | grep -oE '/\S+'); do tgt="$(dirname ${lib})" mkdir -p .${tgt} cp ${lib} .${tgt} done mkdir -p .$(dirname ${executable}) cp ${executable} .${executable} tar cf ../chroot-run-anywhere.tgz .

誰かがすでにAppImageについて言及していたけど、POSIXシェルスクリプトとして実行されるこの別の実装にも注目してほしい。これにより、異なるアーキテクチャで異なるプログラムを動的にディスパッチできるんだ。例えば、ARMとx64用のファットバイナリ。 https://github.com/mgord9518/shappimage

Exodus (https://github.com/intoli/exodus) はこれに良かったけど、最近はPythonエラーを出してる。

デトゥアって初めて聞いた。結構クールなハックだね。

Hacker Newsで議論の続きを見る