Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 18628] ファイルの概要

このコミットは、Go言語のリンカ (cmd/ld) におけるARMv5アーキテクチャ向けのビルド問題を修正するものです。具体的には、動的インポートシンボル (SDYNIMPORT) の到達可能性チェックに関するロジックが修正され、ARMv5環境でのリンカの誤動作を防ぎます。

コミット

commit e2fe968d5f814dd947f5bb2ddca1768171d39a3a
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Tue Feb 25 06:53:17 2014 +1100

    cmd/ld: fix build for ARMv5.
    Credit goes to Dave Cheney for debugging the issue.
    
    LGTM=dave, rsc
    R=dave, rsc
    CC=golang-codereviews
    https://golang.org/cl/67820043

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/e2fe968d5f814dd947f5bb2ddca1768171d39a3a

元コミット内容

cmd/ld: fix build for ARMv5.
Credit goes to Dave Cheney for debugging the issue.

LGTM=dave, rsc
R=dave, rsc
CC=golang-codereviews
https://golang.org/cl/67820043

変更の背景

このコミットは、Go言語のリンカ (cmd/ld) がARMv5アーキテクチャ向けにビルドされる際に発生していた問題を修正するために導入されました。ARMv5は、特に組み込みシステムや古いモバイルデバイスで広く使用されている32ビットのARMプロセッサアーキテクチャです。Go言語はクロスコンパイルを強力にサポートしていますが、特定のアーキテクチャ、特にARMv5のような制約のある環境では、リンカの挙動が問題を引き起こすことがあります。

元のコードでは、dynrelocsym 関数内で動的インポートシンボル (SDYNIMPORT) の到達可能性 (reachable) をチェックする際に、r->sym != S という条件が欠落していました。この欠落により、r->symS (おそらくNULLまたは無効なシンボル) である場合に、r->sym->reachable へのアクセスが発生し、不正なメモリアクセスやクラッシュを引き起こしていた可能性があります。特にARMv5のような環境では、メモリ管理やアラインメントの要件が厳しく、このような細かなバグがビルドプロセス全体を不安定にすることがあります。

この問題はDave Cheneyによってデバッグされ、その修正がShenghou Maによってコミットされました。これは、Go言語のツールチェーンが多様なアーキテクチャで安定して動作するための継続的な努力の一環です。

前提知識の解説

Go言語のリンカ (cmd/ld)

Go言語のビルドプロセスにおいて、リンカ (cmd/ld) は非常に重要な役割を担います。コンパイラによって生成されたオブジェクトファイル(GoのパッケージやCgoでリンクされたCコードなど)を結合し、実行可能なバイナリや共有ライブラリを生成します。リンカの主なタスクには、シンボルの解決(関数や変数のアドレスを決定する)、再配置(コードやデータの参照を正しいアドレスに調整する)、そして最終的な実行可能ファイルの構造化が含まれます。

動的リンクと動的インポートシンボル (SDYNIMPORT)

Go言語はデフォルトで静的リンクを好みますが、Cgoを介してCライブラリと連携する場合など、動的リンクのメカニズムも利用します。動的リンクでは、プログラムの実行時に外部ライブラリの関数やデータがロードされ、そのアドレスが解決されます。

  • 動的再配置 (Dynamic Relocation): コンパイル時には正確なメモリアドレスが不明なシンボル(関数や変数)のアドレスを、実行時にオペレーティングシステムの動的リンカが解決するプロセスです。これにより、複数のプログラムがライブラリの単一コピーをメモリで共有できます。
  • シンボル (Symbol): 関数や変数を表す名前です。
  • SDYNIMPORT: Goリンカの内部で使われるシンボルタイプの一つで、動的にインポートされるシンボル、つまり外部の共有ライブラリからロードされるシンボルを示します。これらのシンボルは、コンパイル時にはその最終的なアドレスが確定せず、実行時に動的リンカによって解決される必要があります。

シンボルの到達可能性 (reachable)

Goリンカは、最終的なバイナリに含めるべきコードとデータを決定するために、シンボルの「到達可能性」を追跡します。プログラムのエントリポイントから到達可能なシンボルのみがバイナリに含まれ、それ以外はデッドコードとして削除されます。reachable フラグは、特定のシンボルがこの到達可能性ツリーに含まれているかどうかを示します。動的インポートシンボルであっても、それがプログラムによって実際に参照され、使用される場合は「到達可能」であるとマークされる必要があります。

ARMv5アーキテクチャ

ARMv5は、ARMアーキテクチャのバージョン5を指します。これは主に32ビットの命令セットを特徴とし、浮動小数点演算ユニット (FPU) がオプションであるか、ソフトウェアエミュレーションに依存することが多いです。Go言語のクロスコンパイルでは、GOOS (オペレーティングシステム) と GOARCH (アーキテクチャ) の他に、GOARM 環境変数を使用してARMのバージョンを指定することがあります(例: GOARM=5)。ARMv5のような古いアーキテクチャでは、メモリ制約、アラインメント要件、特定の命令セットのサポート状況など、現代のアーキテクチャとは異なる考慮事項が必要となるため、リンカやコンパイラが特別な処理を行う必要があります。

技術的詳細

このコミットの技術的な核心は、src/cmd/ld/data.c ファイル内の dynrelocsym 関数にあります。この関数は、動的再配置が必要なシンボルを処理する役割を担っています。

元のコードでは、for ループ内でシンボル r->symSDYNIMPORT タイプであるか、または r->type が256以上(これも動的再配置に関連するタイプ)である場合に、以下の条件分岐がありました。

if(r->sym != S && r->sym->type == SDYNIMPORT || r->type >= 256) {
    if(!r->sym->reachable)
        diag("internal inconsistency: dynamic symbol %s is not reachable.", r->sym->name);
    adddynrel(s, r);
}

問題は、内側の if(!r->sym->reachable) の条件です。この行は、外側の if 文の条件 r->sym != S && r->sym->type == SDYNIMPORT || r->type >= 256 が真である場合に実行されます。しかし、論理演算子の優先順位により、r->sym != Sr->sym->type == SDYNIMPORTr->type >= 256 のOR条件とは独立して評価されます。

具体的には、r->type >= 256 が真である場合、r->sym != S の条件が満たされていなくても、内側の if ブロックに入ってしまいます。このとき、もし r->symS (NULLポインタや無効なポインタ) であった場合、r->sym->reachable へのアクセスはNULLポインタデリファレンスとなり、リンカがクラッシュする原因となります。

ARMv5環境でこの問題が顕在化したのは、おそらく特定のリンキングシナリオや、ARMv5向けのGoランタイムのビルド時に、このような無効な r->sym を持つ再配置エントリが生成されやすかったためと考えられます。

修正は非常にシンプルですが、効果的です。内側の if 文の条件に r->sym != S を追加することで、r->sym が有効なシンボルである場合にのみ r->sym->reachable にアクセスするように変更されました。

if(r->sym != S && r->sym->type == SDYNIMPORT || r->type >= 256) {
    if(r->sym != S && !r->sym->reachable) // ここに r->sym != S が追加された
        diag("internal inconsistency: dynamic symbol %s is not reachable.", r->sym->name);
    adddynrel(s, r);
}

この変更により、r->type >= 256 が真であっても r->symS である場合には、r->sym->reachable へのアクセスが回避され、リンカの安定性が向上しました。これは、Go言語のツールチェーンが堅牢であり、様々なアーキテクチャの微妙な違いにも対応していることを示しています。

コアとなるコードの変更箇所

--- a/src/cmd/ld/data.c
+++ b/src/cmd/ld/data.c
@@ -343,7 +343,7 @@ dynrelocsym(LSym *s)
 
 	for(r=s->r; r<s->r+s->nr; r++) {
 		if(r->sym != S && r->sym->type == SDYNIMPORT || r->type >= 256) {
-			if(!r->sym->reachable)
+			if(r->sym != S && !r->sym->reachable)
 				diag("internal inconsistency: dynamic symbol %s is not reachable.", r->sym->name);
 			adddynrel(s, r);
 		}

コアとなるコードの解説

変更は src/cmd/ld/data.c ファイルの dynrelocsym 関数内の一行です。

  • dynrelocsym(LSym *s): この関数は、与えられたシンボル s に関連する動的再配置を処理します。LSym はリンカが扱うシンボル構造体です。
  • for(r=s->r; r<s->r+s->nr; r++): シンボル s に関連するすべての再配置エントリ r をループで処理します。s->r は再配置エントリの配列の開始ポインタ、s->nr は再配置エントリの数です。
  • if(r->sym != S && r->sym->type == SDYNIMPORT || r->type >= 256): この外側の条件は、現在の再配置エントリ r が動的再配置を必要とするかどうかを判断します。
    • r->sym != S: 再配置が有効なシンボル r->sym を参照していることを確認します。S はおそらくNULLまたは無効なシンボルを表す定数です。
    • r->sym->type == SDYNIMPORT: シンボルが動的インポートシンボルであることを確認します。
    • r->type >= 256: 再配置タイプが特定の閾値以上である場合も動的再配置と見なされます。これは、Goリンカが内部的に使用する再配置タイプの分類に関連します。
  • - if(!r->sym->reachable): 変更前のコードです。この行は、r->symSDYNIMPORT タイプであるか、または r->type >= 256 の場合に実行されます。しかし、もし r->type >= 256 が真であり、かつ r->symS であった場合、r->sym->reachable へのアクセスは不正なメモリアクセスを引き起こします。
  • + if(r->sym != S && !r->sym->reachable): 変更後のコードです。内側の if 文の条件に r->sym != S が追加されました。これにより、r->sym が有効なシンボルである場合にのみ !r->sym->reachable のチェックが行われるようになります。この修正によって、無効なシンボルへのアクセスが防止され、リンカのクラッシュが回避されます。
  • diag("internal inconsistency: dynamic symbol %s is not reachable.", r->sym->name);: もし動的シンボルが到達可能でない場合、内部的な不整合として診断メッセージが出力されます。これは通常、リンカのロジックに問題があることを示唆します。
  • adddynrel(s, r);: 動的再配置エントリをリンカの内部構造に追加します。

この修正は、Goリンカの堅牢性を高め、特にARMv5のような特定のアーキテクチャでのビルドの安定性を確保するために重要でした。

関連リンク

  • Go言語のリンカに関するドキュメント (Go 1.5のリンカの変更点について触れられているが、一般的なリンカの役割を理解するのに役立つ): https://go.dev/doc/go1.5#linker
  • Go言語のARMv5ビルドに関する一般的な課題 (浮動小数点演算、メモリ制約など): https://go.dev/doc/install/source#arm (このリンクは一般的な情報源であり、直接的な問題解決のソースではない可能性がありますが、ARMv5の背景理解に役立ちます)

参考にした情報源リンク