[インデックス 11331] ファイルの概要
このコミットは、Go言語のリンカ (ld
) におけるMach-O形式のバイナリに対するコード署名に関するバグ修正です。具体的には、Cgoを使用しない(純粋なGoの)バイナリにおいて、codesign_allocate
ユーティリティがコード署名時に適切にパディングを処理できない問題を解決します。
コミット
commit 280d85a80bd14e732dee7e9991c35da80f62bed7
Author: Mikkel Krautz <mikkel@krautz.dk>
Date: Mon Jan 23 09:42:09 2012 -0500
ld: fix Mach-O code signing for non-cgo binaries
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5561060
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/280d85a80bd14e732dee7e9991c35da80f62bed7
元コミット内容
Go言語のリンカ (ld
) において、Mach-O形式のバイナリ(macOSなどで使用される実行ファイル形式)のコード署名に関する修正。特に、Cgo(GoとC言語の相互運用機能)を使用しない純粋なGoバイナリのコード署名が正しく行われるように修正された。
変更の背景
この変更の背景には、macOSにおける実行ファイルのコード署名プロセスと、Go言語のリンカが生成するMach-Oバイナリの特性との間の不整合がありました。
macOSでは、アプリケーションの整合性と信頼性を保証するためにコード署名が広く利用されています。コード署名プロセスでは、バイナリの特定のセクション(特に __LINKEDIT
セクション)に署名情報が追加されます。この署名情報を追加する際に、Appleが提供する codesign_allocate
というユーティリティが使用されます。
問題は、codesign_allocate
が、特に純粋なGoバイナリ(Cgoを使用しないバイナリ)に対して、__LINKEDIT
セクションの末尾に LC_CODE_SIGNATURE
ロードコマンドを追加する際に、必要なパディングの計算を誤ることにありました。LC_CODE_SIGNATURE
ロードコマンドは16バイト境界にアラインされている必要があります。動的なMach-Oバイナリ(例えば、Cgoを使用して外部ライブラリにリンクしているGoバイナリや、一般的なC/C++バイナリ)の場合、codesign_allocate
はこのアラインメントを適切に処理できます。しかし、純粋なGoバイナリでは __LINKEDIT
セクションがほとんど空であるため、codesign_allocate
が自身で追加するパディングを考慮に入れず、結果としてコード署名が失敗するか、不正なバイナリが生成される可能性がありました。
このコミットは、Goリンカ側で __LINKEDIT
セクションの末尾を事前に16バイト境界にアラインさせることで、codesign_allocate
が追加のパディングを適用する必要がないようにし、この問題を回避することを目的としています。
前提知識の解説
Mach-O (Mach Object)
Mach-Oは、macOS、iOS、watchOS、tvOSなどのAppleのオペレーティングシステムで使用される実行可能ファイル、オブジェクトコード、共有ライブラリ、ダイナミックロード可能バンドル、およびコアダンプのファイル形式です。WindowsのPE (Portable Executable) やLinuxのELF (Executable and Linkable Format) に相当します。
Mach-Oファイルは、ヘッダ、ロードコマンド、セグメント、セクションなどで構成されます。
- ヘッダ: ファイルの基本的な情報(CPUタイプ、ファイルタイプなど)を含みます。
- ロードコマンド: カーネルがバイナリをメモリにロードする方法を記述します。これには、セグメントの定義、共有ライブラリの依存関係、エントリポイントなどが含まれます。
- セグメント: 実行可能コードやデータを含むメモリ領域の論理的なまとまりです。各セグメントは1つ以上のセクションを含みます。
- セクション: セグメント内のより細かいデータ単位です。例えば、コードセクション (
__text
)、データセクション (__data
)、読み取り専用データセクション (__rodata
) などがあります。
コード署名 (Code Signing)
コード署名とは、ソフトウェアの実行可能ファイルやスクリプトにデジタル署名を付与するプロセスです。これにより、以下の目的が達成されます。
- 整合性の保証: ソフトウェアが署名後に改ざんされていないことを確認できます。
- 信頼性の保証: ソフトウェアが特定の開発元によって作成されたことを証明できます。 macOSでは、Gatekeeperなどのセキュリティ機能がコード署名を利用して、信頼できないソースからのソフトウェアの実行をブロックします。
__LINKEDIT
セクション
Mach-Oバイナリにおける __LINKEDIT
セクションは、動的リンカが実行時に必要とする情報(シンボルテーブル、文字列テーブル、リロケーション情報など)を格納するセクションです。コード署名情報もこのセクションの末尾に追加されることが一般的です。
LC_CODE_SIGNATURE
ロードコマンド
これはMach-Oのロードコマンドの一つで、バイナリのコード署名に関する情報(署名データのオフセットとサイズ)を記述します。このロードコマンドは、Mach-Oファイル内の特定のオフセットに配置され、その配置には16バイト境界のアラインメントが要求されます。
Cgo (Go and C interoperability)
Cgoは、GoプログラムからC言語のコードを呼び出したり、C言語のコードからGoの関数を呼び出したりするためのGoの機能です。Cgoを使用すると、Goプログラムは既存のCライブラリを利用したり、パフォーマンスが重要な部分をCで記述したりすることができます。
- Cgoバイナリ: Cgoを使用しているGoプログラムは、コンパイル時にCコンパイラ(通常はGCCやClang)によってCコードがコンパイルされ、Goのコードとリンクされます。これにより、生成されるバイナリはCライブラリへの依存性を持つことが多く、動的リンカが必要とする情報が増えるため、
__LINKEDIT
セクションも大きくなる傾向があります。 - 非Cgoバイナリ (純粋なGoバイナリ): Cgoを使用しないGoプログラムは、完全にGo言語で記述されており、Goランタイムと標準ライブラリのみに依存します。これらのバイナリは通常、自己完結型であり、外部のCライブラリへの依存性がありません。そのため、
__LINKEDIT
セクションが非常に小さくなることがあります。
codesign_allocate
ユーティリティ
codesign_allocate
は、Appleの開発者ツールに含まれるユーティリティで、Mach-Oバイナリにコード署名情報を追加するために使用されます。このツールは、バイナリの __LINKEDIT
セクションの末尾に LC_CODE_SIGNATURE
ロードコマンドと実際の署名データを挿入します。この際、LC_CODE_SIGNATURE
ロードコマンドが16バイト境界にアラインされるように、必要に応じてパディングを追加する役割も担います。
技術的詳細
このコミットの技術的な核心は、Mach-Oバイナリの __LINKEDIT
セクションの末尾のアラインメントを修正することにあります。
元のコードでは、s4->size%4
という条件で __LINKEDIT
セクションの末尾(具体的には .dynstr
セクションのサイズ)が4バイト境界にアラインされるようにパディング(0バイト)を追加していました。これは、一部のMach-Oの要件や、古いシステムでのアラインメント要件に合致していた可能性があります。
しかし、macOSのコード署名プロセスにおいて、codesign_allocate
ユーティリティが LC_CODE_SIGNATURE
ロードコマンドを挿入する際には、このロードコマンドが16バイト境界にアラインされている必要があります。
純粋なGoバイナリの場合、__LINKEDIT
セクションは非常に小さく、特に .dynstr
セクションのサイズが小さいことが多いため、4バイト境界のアラインメントでは16バイト境界のアラインメントが保証されません。
コミットのコメントによると、codesign_allocate
は動的なMach-Oバイナリ(Cgoを使用するGoバイナリなど)に対しては、自身が追加するパディングを考慮して16バイトアラインメントを適切に処理できます。しかし、純粋なGoバイナリのように __LINKEDIT
セクションがほとんど空の場合、codesign_allocate
は自身が追加するパディングを正しく計算できず、結果として LC_CODE_SIGNATURE
ロードコマンドが16バイト境界にアラインされないというバグがありました。
この修正では、while(s4->size%4)
を while(s4->size%16)
に変更することで、Goリンカが生成するMach-Oバイナリの __LINKEDIT
セクションの末尾を、最初から16バイト境界に強制的にアラインするようにしました。これにより、codesign_allocate
が追加のパディングを適用する必要がなくなり、そのバグを回避できるようになります。結果として、純粋なGoバイナリもmacOS上で正しくコード署名できるようになります。
コアとなるコードの変更箇所
変更は src/cmd/ld/macho.c
ファイルの domacholink
関数内で行われています。
--- a/src/cmd/ld/macho.c
+++ b/src/cmd/ld/macho.c
@@ -499,7 +499,24 @@ domacholink(void)
s3 = lookup(".linkedit.got", 0);
s4 = lookup(".dynstr", 0);
- while(s4->size%4)
+ // Force the linkedit section to end on a 16-byte
+ // boundary. This allows pure (non-cgo) Go binaries
+ // to be code signed correctly.
+ //
+ // Apple's codesign_allocate (a helper utility for
+ // the codesign utility) can do this fine itself if
+ // it is run on a dynamic Mach-O binary. However,
+ // when it is run on a pure (non-cgo) Go binary, where
+ // the linkedit section is mostly empty, it fails to
+ // account for the extra padding that it itself adds
+ // when adding the LC_CODE_SIGNATURE load command
+ // (which must be aligned on a 16-byte boundary).
+ //
+ // By forcing the linkedit section to end on a 16-byte
+ // boundary, codesign_allocate will not need to apply
+ // any alignment padding itself, working around the
+ // issue.
+ while(s4->size%16)
adduint8(s4, 0);
size = s1->size + s2->size + s3->size + s4->size;
コアとなるコードの解説
変更された行は以下の部分です。
- while(s4->size%4)
+ while(s4->size%16)
s4
は.dynstr
セクション(動的文字列テーブル)を表すシンボルです。Mach-Oの__LINKEDIT
セクションは、この.dynstr
セクションの後に続く形で構成されることが多いため、.dynstr
のサイズを調整することで__LINKEDIT
セクション全体の末尾のアラインメントを制御できます。s4->size
は.dynstr
セクションの現在のサイズです。%
は剰余演算子です。adduint8(s4, 0)
は、s4
が指すセクションに1バイトのゼロパディングを追加する関数です。
元のコード while(s4->size%4)
は、.dynstr
セクションのサイズが4の倍数になるまでゼロパディングを追加していました。これは、セクションのサイズを4バイト境界にアラインするための処理です。
修正後のコード while(s4->size%16)
は、このアラインメントの要件を4バイトから16バイトに引き上げました。これにより、.dynstr
セクションのサイズが16の倍数になるまでゼロパディングが追加されます。結果として、__LINKEDIT
セクション全体の末尾が16バイト境界にアラインされることが保証されます。
この変更により、codesign_allocate
ユーティリティが LC_CODE_SIGNATURE
ロードコマンドを挿入する際に、すでに適切なアラインメントが確保されているため、codesign_allocate
自身がパディングを計算して追加する必要がなくなります。これにより、純粋なGoバイナリのコード署名が正しく行われるようになります。
関連リンク
- Go言語の公式ウェブサイト: https://golang.org/
- Go言語のIssue Tracker (Go CL 5561060): https://golang.org/cl/5561060 (コミットメッセージに記載されているリンク)
- Mach-O File Format Reference (Apple Developer Documentation): https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html (一般的な情報源)
- Code Signing Guide (Apple Developer Documentation): https://developer.apple.com/library/archive/documentation/Security/Conceptual/CodeSigningGuide/Introduction/Introduction.html (一般的な情報源)
参考にした情報源リンク
- 上記の「関連リンク」セクションに記載されている公式ドキュメントやGoの変更リスト。
- Mach-Oファイル形式、コード署名、Cgoに関する一般的な技術記事やブログ投稿(具体的なURLは省略しますが、これらの概念を理解するために広く参照される情報源です)。
codesign_allocate
ユーティリティの動作に関する情報(Appleのドキュメントや関連する開発者フォーラムなど)。- Go言語のリンカのソースコード(
src/cmd/ld/macho.c
)の分析。 - Go言語のコンパイルとリンクに関する一般的な知識。