linux/ppc64(ビッグエンディアン)ポートがELFv1からELFv2システムABIへ移行し、cgo・PIE・外部リンクが利用可能になった。
PowerPC
概要
Linux上のビッグエンディアン64ビットPowerPCポート(GOOS=linux GOARCH=ppc64)において、Goツールチェーンが生成するバイナリがELFv2システムABIを使用するようになった。ELFv2のサポートにはLinuxカーネル3.13以降が必要である(RHEL7は3.10カーネルにこのサポートをバックポート済み)。
これに伴い、cgo・位置独立実行ファイル(PIE)・外部リンクがlinux/ppc64でも利用可能になった。これらの機能を使うにはELFv2互換のランタイム(libcおよびリンク・ロードされる全ライブラリ)が必要である。cgoを使わないプログラムについては、Goツールチェーンは引き続きデフォルトで内部リンクによる静的バイナリを生成する。
導入経緯
当初のproposalは「アクティブにサポートされているpowerpc64 ELFv1 Linuxディストリビューションが存在しない」ことを理由に、Go 1.27でlinux/ppc64(ビッグエンディアン)ポート自体を廃止するという内容だった。これは一旦acceptedとなり、Go 1.26のリリースノートに事前告知を追加する作業も進められた。
しかしその後、ArchPOWERやChimera Linux、Gentoo(musl)、Adélie LinuxなどELFv2ベースのビッグエンディアンppc64ディストリビューションが実際に使われているとのコメントが寄せられ、ポートを廃止する代わりにELFv1からELFv2へ切り替えることで存続させてはどうかという代替案が提示された。既存のlinux/ppc64は内部リンクのみでcgoも外部リンクも使っていなかったため、ELFv2への切り替えは既存の静的バイナリに対して透過的であると判断された。
議論の結果、issueは「ELFv1からELFv2への移行」に焦点を絞る形でリタイトルされ、GOOS/GOARCHのターゲット名(linux/ppc64)自体は変更せずにABIのみ切り替える方針で再度likely accept、続いてacceptedとなった。Go 1.26で事前告知を行い、実際の切り替えはGo 1.27で行われている。なお、このポートはsecondary portであり、ビルダーの維持にはコミュニティの協力が引き続き必要とされている。
議論のハイライト
- 提案当初はlinux/ppc64(ビッグエンディアン)ポートの廃止が提案されていたが、実際にELFv2ベースのビッグエンディアンppc64ディストリビューションを使っているユーザーからのフィードバックを受け、ABIをELFv2に切り替えて存続させる方針に転換した。
- 既存のlinux/ppc64は内部リンクのみでcgoや外部リンクを使っていなかったため、ELFv1からELFv2への切り替えはユーザーから見て透過的な変更になると判断された。
- ELFv2への切り替え自体の実装は小さく、既存のlinux/ppc64le・openbsd/ppc64のELFv2サポートを流用できた。
- ポートの存続にはコミュニティによるビルダーの提供・保守が引き続き条件となっており、実際にOSUOL(Oregon State University Open Source Lab)と連携してELFv2環境(ArchPOWERベース)のビルダーへの移行が進められた。
移行時の注意
- ELFv2システムABIを利用するにはLinuxカーネル3.13以降が必要である。RHEL7は3.10カーネルにこのサポートをバックポート済みのため利用できる。
- cgo・PIE・外部リンクを使う場合は、libcを含めリンク・ロードされる全ライブラリがELFv2互換である必要がある。
- cgoを使わないプログラムはこれまで通りデフォルトで内部リンクの静的バイナリが生成されるため、影響を受けない。
- cgoオプションを持つプログラムで、純粋なGoの静的バイナリが必要な場合は、
go build実行時に環境変数CGO_ENABLED=0を設定すればよい。
実装解説
cmd/link/internal/ppc64.Initを見ると、GOARCH=ppc64のときの動的リンカパス(Linuxdynld)・musl版パス(LinuxdynldMusl)がppc64leと同じ/lib64/ld64.so.2・/lib/ld-musl-powerpc64.so.1に設定されている。ELFv2の動的リンカパスはppc64leで従来から使われてきたものと共通であり、ここでppc64とppc64leが同一のELFv2前提のパスを共有していることが確認できる。
また、archinitではobjabi.Hlinux(linux向けELFヘッダ生成)をobjabi.Hopenbsd(既にELFv2のopenbsd/ppc64)と同じ分岐で処理しており、ppc64向けのELF初期化コード(ld.Elfinit)がppc64le/openbsdと共通化されている。ppc64固有のELFv1向け分岐は見当たらず、リンカのppc64パッケージ内ではppc64とppc64leの扱いがELFv2を前提に統一されている。
さらにinternal/platform.BuildModeSupportedでは、c-shared(l.165)・pie(l.183)・shared(l.197)・plugin(l.204)の各buildmodeの対応プラットフォーム一覧にlinux/ppc64がlinux/ppc64leと並んで含まれており、InternalLinkPIESupported(l.221)にも同様に含まれている。internal/platform.zosarch.goでは{"linux", "ppc64"}のエントリにCgoSupported: trueが設定されており、これらの記述はlinux/ppc64でcgo・PIE・shared/plugin buildmodeが利用可能になっているという本項目の内容と一致する。