
2026/01/26 16:41
**Linux バイナリ互換性の聖杯:Musl と `dlopen`** *概要 * Musl は軽量な C ライブラリで、静的リンクと小さなバイナリサイズを実現するよう設計されています。 * `dlopen` は実行時に共有ライブラリを動的ロードできる機能で、モジュール化されたアプリケーションを可能にします。 *主なメリット * **バイナリサイズの削減** – 静的リンクされた Musl バイナリは多くの場合 < 5 MB です。 * **決定論的ビルド** – Musl の純粋 C 実装により、コンパイラ固有の不具合を回避できます。 * **動的柔軟性** – `dlopen` は再コンパイル不要でオプションのプラグインやドライバをロード可能です。 *一般的なユースケース * ストレージが限られた組込みシステム。 * 攻撃面を最小化したいコンテナ環境。 * 実行時にプラグインを追加するアーキテクチャ(例:ウェブサーバ、メディアプレイヤー)。 *典型的なワークフロー 1. コアアプリケーションを Musl でコンパイル (`musl-gcc -static`)。 2. プラグインを共有オブジェクトとしてビルド (`gcc -fPIC -shared`)。 3. 実行時に `dlopen` でプラグインをロードし、`dlsym` でシンボルを取得。 *注意点と対策 * **互換性のギャップ** – Musl は glibc の拡張機能すべてをサポートしていない可能性があります。十分にテストしてください。 * **シンボル可視性** – エクスポート関数は `__attribute__((visibility("default")))` を付与しておく必要があります。 * **エラーハンドリング** – `dlopen` の戻り値を必ず確認し、`dlerror()` で詳細を調べてください。 *参考リソース * Musl公式サイト: https://www.musl-libc.org/ * GNU `dlopen` マニュアル: https://man7.org/linux/man-pages/man3/dlopen.3.html Musl の軽量静的バイナリと `dlopen` の動的パワーを組み合わせることで、コンパクトなデプロイと実行時拡張性の両立が可能になります。これにより、モダンな Linux アプリケーションで求められる「小ささ」と「柔軟さ」のバランスを実現できます。
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
記事では、Go を使用して完全に静的なプログラムを構築し、Godot を利用して glibc か musl のいずれかに依存する Linux ディストリビューション上で動作させる方法について説明しています。
- 背景: Go はカーネル ≥ 3.2 用に静的コンパイルできますが、GPU ドライバやグラフィックスライブラリは特定の C ライブラリ(glibc または musl)にリンクする必要があります。これらを混在させると失敗します——glibc バイナリは musl システムで動作せず、逆も同様です。
- 問題と対処法:
やc‑shared
モードでの初期試行が musl では失敗したため、著者は graphics.gd 用にビルドオーバーレイ(c‑archive
)を導入しました。最終的な解決策として、共有 C モードを廃止し、Go コードを Godot の静的アーカイブと直接リンクさせることで、glibc と musl 両方の libc 実装に対応する単一バイナリを生成しました。GOOS=musl - 配布上の問題: このバイナリはカーネル ≥ 3.2 であればどんな Linux 上でも動作しますが、完全に静的な graphics.gd バイナリは失敗します。Godot は X11/Wayland/OpenGL/Vulkan 用に
を使用し、musl は静的モードでダイナミックリンカを提供できません(TLS の非互換性)。そのため、glibc と musl 両方のリンク済みビルドを公開する必要があります。dlopen - 将来の修正案:
が弱いシンボルであることから、著者はホストのダイナミックリンカをロードし、アセンブリトランペット経由でシステム libc の TLS に切り替える小さな C ヘルパーを埋め込む提案をしています。これが統合されれば、musl +dlopen
でハードウェアアクセラレーション付きのグラフィックスを持つ真に静的バイナリが実現できるでしょう。dlopen - デモと使用方法: 「Dodge The Creeps」プロジェクト(
)のサンプルビルドは、GCC がインストールされた任意の Linux システムで実行・レンダリングできます。ユーザーはhttps://release.graphics.gd/dodge_the_creeps.static
(GOOS=musl GOARCH=amd64 gd build
を削除して新しい musl プレセットを追加)を使用して、自分のプロジェクトを musl 向けにクロスコンパイルできます。export_presets.cfg
本文
Go + Godot を使ってネイティブかつインストール可能な Android・iOS バイナリ(プロプライエタリ SDK なし)を作るのは、実際にあまり難しくありませんでした。そこで、本格的な挑戦に移ります…
Linux のバイナリ互換性
(参考文献:https://jangafx.com/insights/linux-binary-compatibility)
ここ数年、Linux 用のコマンドラインソフトウェアやサーバーを安定して配布することは非常に簡単です。
go build を実行すれば、カーネル 3.2(2012 年リリース)以降で動く単一の静的バイナリが出来上がります。問題は、ハードウェアアクセラレーション付きグラフィックスを使いたいときに現れます。Linux の GPU ドライバーはすべて C ABI を介して動的ライブラリへアクセスします。この C ライブラリは特定の libc(主に glibc、あるいは musl ベースのディストリビューション)に対してビルドされます。glibc 用にコンパイルしたライブラリや実行ファイルは musl システムで動かず、逆も同様です—明らかな非互換性です。
私自身が直接経験しました。最近、個人用 PC の OS を Void Linux の musl エディションに置き換えました。musl で Zed エディタをビルドするのは大変でしたし、
graphics.gd プロジェクトも同様に壊れがちです。Go は musl 向けに c‑shared や c‑archive を正しくサポートしていません。
この問題には二つの理由があります:
- これは現在使用しているディストリビューションであり、
プロジェクトをビルドできる必要がある。graphics.gd - musl は glibc より静的リンクに優れています。Linux バイナリ互換性の問題解決策は、おそらく musl に関係しているでしょう。
musl のサポート
Go で musl の問題を回避するため、
GOOS=musl 用にビルドオーバーレイを適用したランタイムパッチを導入しました。これは graphics.gd に追加した新しい GOOS です。
次に、musl 用の
c‑shared ビルドを廃止しました。これらは公式 Godot バイナリに Go を簡単に組み込めるため便利でしたが、Godot Foundation は公式 musl ビルドを提供していません。その代わり、Go コードを直接 Godot の c-archive とリンクし、単一のバイナリを作成しました。
驚き: これで
graphics.gd が musl をサポートします!
ただし問題があります。Linux 用にプロジェクトを公開したい場合、glibc ビルドと musl ビルドの両方を用意して、ユーザーにどちらを選ぶか伝える必要があります。Void Linux を導入する前は musl と glibc の違いすら完全には理解できておらず、結果として問題を増やしてしまったように感じます。
単一静的バイナリ + グラフィックス
ここで止まります。musl のメリットの一つは優れた静的ライブラリサポートです。
graphics.gd プロジェクトを単一の静的バイナリにビルドできる方法があるはずです。
実際、Godot は Linux で全ての依存関係を含みます。その他は実行時に動的ロードされますので、
-static フラグを付ければ…
ERROR Dynamic loading not supported
Godot は X11、Wayland、OpenGL、Vulkan 等とのインタフェースに
dlopen を使用します。musl は TLS(スレッドローカルストレージ)の実装が異なるため、静的バイナリで dlopen の実装を拒否します。
しかし心配は不要です!
dlopen は弱シンボルとしてコンパイルされるので、graphics.gd がそれを実装していれば、単一の静的バイナリが 3.2+ Linux システム上で動作する可能性があります。
聖杯
これには先例があります。C の detour 技術では
dlopen を使って SDL をロードし、標準ライブラリ無しでグラフィックスを表示できます。Cosmopolitan の dlopen も同様の手法を採用しています。したがって、ここでの解決策は musl 用にこれを拡張することです。
具体的には、ターゲットマシン向けに小さな C プログラム(またはコンパイル済み)を含めます。このプログラムをロードし、同一プロセスから実行します。ホストの動的リンカを取り込み、システムの
dlopen を盗用して graphics.gd に戻ります。呼び出し期間中はアセンブリトランポリンでシステム libc の TLS へ切り替えます。全体像は cgo と似ています。
多くの試行錯誤と LLM(大規模言語モデル)の活用を経て、musl +
dlopen が Linux 用に単一静的バイナリ+グラフィックスを実現するために必要なすべてであることが判明しました。これで Go の単一静的バイナリ体験とハードウェアアクセラレーション付きグラフィックスの完全サポートが Linux で可能になります。
試してみる
以下は、GCC がインストールされている任意の Linux システム上で実行(およびグラフィックス描画)できるべき
graphics.gd の「Dodge The Creeps」サンプルプロジェクトのビルドです。まだヘルパーバイナリは埋め込んでいません。
https://release.graphics.gd/dodge_the_creeps.static
任意のプラットフォームで自分のプロジェクトをクロスコンパイルすることもできます:
GOOS=musl GOARCH=amd64 gd build
export_presets.cfg を削除して、新しい musl エクスポートプリセットがプロジェクトに追加されるようにしてください。