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

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

このコミットは、Go言語のリンカ (cmd/ld) における src/cmd/ld/data.c ファイルの変更に関するものです。具体的には、リロケーション(再配置)のアドレス範囲チェックを一時的に無効化する修正が行われています。

コミット

commit 396d3af8d0f7d28beedebcff28276f564884341d
Author: Rob Pike <r@golang.org>
Date:   Tue Apr 30 00:19:21 2013 -0700

    cmd/ld: disable relocation range check so build can go green while we debug the issue.
    
    R=golang-dev, dsymonds, dave
    CC=golang-dev
    https://golang.org/cl/9038043

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

https://github.com/golang/go/commit/396d3af8d0f7d28beedebcff28276f564884341d

元コミット内容

cmd/ld: disable relocation range check so build can go green while we debug the issue.

R=golang-dev, dsymonds, dave
CC=golang-dev
https://golang.org/cl/9038043

変更の背景

このコミットは、Go言語のリンカ (cmd/ld) において、特定の環境(特にARMおよびWindows)で発生していたビルドエラーを一時的に回避するために行われました。コミットメッセージにある「relocation range check」が原因でビルドが失敗しており、その問題をデバッグする間、ビルドが成功する状態("go green")を維持するために、一時的にこのチェックを無効化する必要がありました。

リンカにおけるリロケーション範囲チェックは、生成される実行可能ファイルやライブラリ内のアドレス参照が、対象アーキテクチャやリロケーションタイプが許容する範囲内に収まっているかを検証する重要なプロセスです。このチェックが失敗するということは、何らかの理由でリンカが生成しようとしているアドレスが、期待される範囲を超えていることを意味します。これは、コンパイラやリンカのバグ、あるいは特定のアーキテクチャ(この場合はARM)におけるアドレス計算の特殊性、またはWindows環境でのリンカの挙動に関連する問題である可能性が考えられます。

このコミットは、根本的な問題を解決するのではなく、問題のデバッグと修正のための時間を稼ぐための暫定的な措置として導入されました。

前提知識の解説

リンカ (Linker)

リンカは、コンパイラによって生成された複数のオブジェクトファイル(機械語コードとデータを含む)やライブラリを結合し、実行可能なプログラムや共有ライブラリを生成するソフトウェアツールです。リンカの主な役割は以下の通りです。

  1. シンボル解決: オブジェクトファイル間で参照されている関数や変数のアドレスを解決します。例えば、あるオブジェクトファイルで定義された関数を別のオブジェクトファイルから呼び出す場合、リンカはその関数の実際のアドレスを特定し、呼び出し元のアドレスを修正します。
  2. 再配置 (Relocation): プログラム内のアドレス参照を、最終的なメモリ配置に合わせて調整します。コンパイル時には、コードやデータがメモリ上のどこに配置されるか不明なため、相対アドレスや仮のアドレスが使用されます。リンカは、これらを実際のロードアドレスに基づいて絶対アドレスに変換します。
  3. 実行可能ファイルの生成: 解決されたシンボルと再配置されたコード・データを結合し、OSがロードして実行できる形式(例: ELF, PE, Mach-O)のファイルを生成します。

リロケーション (Relocation)

リロケーションとは、プログラムがメモリにロードされる際に、コードやデータ内のアドレス参照を実際のロードアドレスに合わせて調整するプロセスです。コンパイル時やリンク時には、プログラムがメモリ上のどこに配置されるか確定していないため、リンカはリロケーションエントリと呼ばれる情報を使用して、後で修正が必要なアドレス参照を記録します。

リロケーションには様々なタイプがあり、それぞれが特定のアドレス計算やオフセットの適用方法を定義しています。例えば、ある関数へのジャンプ命令のアドレスを修正したり、グローバル変数のアドレスを埋め込んだりします。

リロケーション範囲チェック (Relocation Range Check)

リロケーション範囲チェックは、リンカが再配置を行う際に、計算された最終的なアドレスが、そのリロケーションタイプや対象アーキテクチャが許容するアドレス範囲内に収まっているかを検証するプロセスです。

例えば、32ビットのオフセットしか格納できない命令に対して、参照先のアドレスがその32ビットで表現できる範囲を超えている場合、リンカは「relocation truncated to fit」(リロケーションが収まらないため切り捨てられた)といったエラーを報告します。これは、生成されるコードが正しく動作しない可能性があるため、非常に重要なチェックです。

このチェックは、特に異なるアーキテクチャ間でのクロスコンパイルや、非常に大きなプログラムをリンクする際に問題となることがあります。

技術的詳細

このコミットは、Go言語のリンカ (cmd/ld) の src/cmd/ld/data.c ファイル(現在のGoのソースコードでは、リンカの主要部分はGo言語で書かれており、go/src/cmd/link/internal/ld/data.gogo/src/cmd/link/internal/ld/lib.go などに相当する機能が実装されていますが、このコミット当時はC言語で書かれていた部分があったことを示唆しています)内のリロケーション処理の一部を変更しています。

具体的には、relocsym 関数内で、サイズが4バイトのリロケーション(case 4:)に対するアドレス範囲チェックを無効化しています。元のコードでは、計算されたオフセット o が32ビット符号付き整数 (int32) の範囲に収まっているかを o != (int32)o という条件で確認していました。この条件は、oint32 の範囲を超えている場合に真となります。

コミットの変更は、この条件式の前に 0 && を追加することで、常にこの条件が偽となるようにしています。つまり、if(0 && o != (int32)o)if(false) と同等になり、そのブロック内のエラー診断 (diag("relocation address is too big: %#llx", o);) が実行されなくなります。

この変更は、ARMおよびWindows環境でこのチェックが誤ってトリガーされ、ビルドが失敗していた問題に対応するためのものです。リンカが誤ったアドレス範囲エラーを報告していた可能性があり、その原因を特定・修正するまでの間、ビルドプロセスを継続させるための緊急措置として導入されました。

この一時的な無効化は、潜在的な問題(実際のアドレスが範囲外であるにもかかわらずエラーが報告されない)を抱えることになりますが、ビルドが完全に停止するよりは良いという判断がなされたと考えられます。根本的な解決策は、このチェックがなぜ誤ってトリガーされるのか、またはリンカが生成するアドレスがなぜ範囲外になるのかをデバッグし、修正することです。

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

--- a/src/cmd/ld/data.c
+++ b/src/cmd/ld/data.c
@@ -259,7 +259,8 @@ relocsym(Sym *s)
 			cursym = s;
 			diag("bad reloc size %#ux for %s", siz, r->sym->name);
 		case 4:
-			if(o != (int32)o) {
+			// TODO: Test is causing breakages on ARM and Windows. Disable for now.
+			if(0 && o != (int32)o) {
 				cursym = S;
 				diag("relocation address is too big: %#llx", o);
 			}

コアとなるコードの解説

変更された行は以下の部分です。

-			if(o != (int32)o) {
+			// TODO: Test is causing breakages on ARM and Windows. Disable for now.
+			if(0 && o != (int32)o) {
  1. if(o != (int32)o):

    • o は、リロケーションによって計算されたオフセットまたはアドレスを表す変数です。この変数は、おそらく int64uint64 のような、より広い範囲の値を保持できる型であると推測されます。
    • (int32)o は、o の値を32ビット符号付き整数にキャストしています。
    • この条件式 o != (int32)o は、o の値が32ビット符号付き整数の表現範囲(約 -20億から +20億)を超えている場合に true となります。つまり、計算されたアドレスが32ビットで表現できないほど大きい場合にエラーを検出するためのチェックでした。
  2. if(0 && o != (int32)o):

    • 0 && が条件式の先頭に追加されています。
    • C言語において、0false と評価されます。論理AND演算子 (&&) は、左側のオペランドが false であれば、右側のオペランドを評価せずに結果を false とします(ショートサーキット評価)。
    • したがって、0 && (任意の条件) は常に false となります。
    • これにより、if 文のブロック内のコード(diag("relocation address is too big: %#llx", o);)は実行されなくなり、リロケーションアドレスが大きすぎるというエラーが報告されなくなります。
  3. コメント // TODO: Test is causing breakages on ARM and Windows. Disable for now.:

    • このコメントは、この変更が一時的なものであること、そしてARMおよびWindows環境でのビルドの破損("breakages")が原因であることを明確に示しています。
    • TODO は、将来的にこの問題を根本的に解決する必要があることを示唆しています。

この変更は、リンカが本来検出するべき潜在的なアドレス範囲エラーを一時的に無視することで、ビルドを成功させるためのものです。これはデバッグや開発の進行を妨げないための実用的なアプローチですが、根本的な問題が解決されるまでは、生成されるバイナリに潜在的な問題が残る可能性があります。

関連リンク

参考にした情報源リンク