[インデックス 13867] ファイルの概要
本コミットは、Go言語のリンカ(cmd/ld
)に対する変更であり、データ競合検出機能(Race Detector)をサポートするためのものです。具体的には、リンカに-b
フラグが渡された際に、runtime/race
パッケージへの依存関係を自動的に追加するように修正されています。これにより、Goプログラムのビルド時にデータ競合検出を有効化するための基盤が構築されます。
コミット
commit cc8cfefd8ade736ee75673a16da44575499290f7
Author: Dmitriy Vyukov <dvyukov@google.com>
Date: Wed Sep 19 01:05:25 2012 +0400
race: linker changes
This is the second part of a bigger change that adds data race detection feature:
https://golang.org/cl/6456044
This change makes the linker emit dependency on runtime/race package when supplied with -b flag.
R=rsc, minux.ma
CC=golang-dev
https://golang.org/cl/6488074
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/cc8cfefd8ade736ee75673a16da44575499290f7
元コミット内容
このコミットは、Go言語にデータ競合検出機能を追加する大規模な変更の第2段階です。リンカが-b
フラグを受け取った際に、runtime/race
パッケージへの依存関係を出力するように変更します。
変更の背景
Go言語は並行処理を強力にサポートしていますが、並行処理はデータ競合(data race)というバグを引き起こす可能性があります。データ競合とは、複数のゴルーチンが同時に同じメモリ位置にアクセスし、少なくとも1つのアクセスが書き込みであり、かつそれらのアクセスが同期メカニズムによって保護されていない場合に発生する状況です。データ競合はプログラムの予測不能な動作やクラッシュの原因となり、デバッグが非常に困難です。
このコミットは、Go言語に組み込まれたデータ競合検出機能(Go Race Detector)を導入する一環として行われました。Race Detectorは、実行時にデータ競合を検出し、開発者に警告することで、並行処理バグの特定と修正を支援します。この機能を実現するためには、コンパイルされたプログラムが特別なランタイムライブラリとリンクされる必要があり、本コミットはそのためのリンカ側の変更を実装しています。具体的には、ビルド時に-race
フラグ(コミットメッセージでは-b
フラグと記載されているが、後のバージョンで-race
に変更された)をGoツールチェインに渡すことで、Race Detectorが有効になるように設計されています。
前提知識の解説
- リンカ (Linker): リンカは、コンパイラによって生成されたオブジェクトファイルやライブラリを結合し、実行可能なプログラムを生成するツールです。シンボル解決(未定義の関数や変数を定義と結びつける)や、ライブラリの組み込みなどを行います。Go言語のツールチェインでは、
cmd/ld
がリンカの役割を担っています。 - データ競合 (Data Race): 前述の通り、複数の並行実行される処理(Goではゴルーチン)が、同期なしに共有メモリにアクセスし、少なくとも1つが書き込みである場合に発生するバグです。
- Go Race Detector: Go言語に組み込まれているデータ競合検出ツールです。プログラムの実行時にデータ競合を検出し、詳細なレポートを出力します。これは、コンパイル時に特別なインストゥルメンテーション(計測コードの埋め込み)と、専用のランタイムライブラリ(
runtime/race
)をリンクすることで実現されます。 runtime/race
パッケージ: Goのランタイムの一部であり、データ競合検出機能の実装が含まれています。このパッケージは、プログラムのメモリアクセスを監視し、競合を検出するためのロジックを提供します。go tool nm
: Goのバイナリファイル内のシンボルテーブルを表示するツールです。リンカのデバッグや、特定のシンボルがどのように解決されているかを確認する際に役立ちます。smprint
: Goリンカの内部関数で、C言語のsprintf
に似た文字列フォーマット関数です。
技術的詳細
このコミットの技術的な核心は、Goリンカが-b
フラグ(後の-race
フラグ)を認識し、それに応じてビルドプロセスを変更する点にあります。
-
リンカオプションの追加 (
src/cmd/ld/doc.go
): リンカのドキュメントファイルsrc/cmd/ld/doc.go
に、新しいオプション-b
が追加されました。このオプションは「Link with race detection libraries.」(競合検出ライブラリとリンクする)と説明されています。これにより、ユーザーはリンカに-b
フラグを渡すことで、Race Detectorを有効にできることが明示されます。 -
ランタイムライブラリのパス変更 (
src/cmd/ld/lib.c
):src/cmd/ld/lib.c
のlibinit
関数内で、リンカが標準ライブラリを検索するパスが動的に変更されるようになりました。char *race;
という変数が導入されます。if(debug['b']) race = "_race";
という条件文が追加され、リンカのデバッグフラグ'b'
が設定されている場合(つまり-b
フラグが渡された場合)に、race
変数が"_race"
という文字列に設定されます。Lflag(smprint("%s/pkg/%s_%s%s", goroot, goos, goarch, race));
という行が変更され、ライブラリパスの最後にrace
変数の値が追加されるようになりました。-b
フラグがない場合:$GOROOT/pkg/goos_goarch
-b
フラグがある場合:$GOROOT/pkg/goos_goarch_race
これにより、リンカはRace Detectorが有効な場合に、_race
サフィックスが付いた特別なバージョンの標準ライブラリ(例えば、linux_amd64_race
)を探すようになります。これらのライブラリは、Race Detectorのインストゥルメンテーションが施されたコードを含んでいます。
-
runtime/race
パッケージの明示的なリンク (src/cmd/ld/lib.c
):src/cmd/ld/lib.c
のloadlib
関数内で、if(debug['b']) loadinternal("runtime/race");
という行が追加されました。 これは、-b
フラグが設定されている場合に、リンカがruntime/race
パッケージを明示的に内部的にロードすることを意味します。loadinternal
は、リンカがGoの標準ライブラリの一部として認識しているパッケージをロードするための関数です。これにより、Race Detectorのコア機能を提供するruntime/race
パッケージが、最終的な実行可能ファイルに確実に含まれるようになります。
これらの変更により、Goのビルドシステムは、-b
フラグ(後の-race
フラグ)が指定された際に、データ競合検出に必要な特別なランタイムライブラリと、インストゥルメンテーションされた標準ライブラリを自動的にリンクするようになります。
コアとなるコードの変更箇所
src/cmd/ld/doc.go
--- a/src/cmd/ld/doc.go
+++ b/src/cmd/ld/doc.go
@@ -56,5 +56,7 @@ Options new in this version:
Set the value of an otherwise uninitialized string variable.
The symbol name should be of the form importpath.name,
as displayed in the symbol table printed by "go tool nm".
+ -b
+ Link with race detection libraries.
*/
package documentation
src/cmd/ld/lib.c
--- a/src/cmd/ld/lib.c
+++ b/src/cmd/ld/lib.c
@@ -72,6 +72,8 @@ Lflag(char *arg)
void
libinit(void)
{
+ char *race;
+
fmtinstall('i', iconv);
fmtinstall('Y', Yconv);
fmtinstall('Z', Zconv);
@@ -80,7 +82,10 @@ libinit(void)
print("goarch is not known: %s\n", goarch);
// add goroot to the end of the libdir list.
- Lflag(smprint("%s/pkg/%s_%s", goroot, goos, goarch));
+ race = "";
+ if(debug['b'])
+ race = "_race";
+ Lflag(smprint("%s/pkg/%s_%s%s", goroot, goos, goarch, race));
// Unix doesn't like it when we write to a running (or, sometimes,
// recently run) binary, so remove the output file before writing it.
@@ -281,6 +286,8 @@ loadlib(void)
loadinternal("runtime");
if(thechar == '5')
loadinternal("math");
+ if(debug['b'])
+ loadinternal("runtime/race");
for(i=0; i<libraryp; i++) {
if(debug['v'])
コアとなるコードの解説
-
src/cmd/ld/doc.go
の変更: リンカのドキュメントに-b
フラグの説明が追加されました。これは、ユーザーがこの新しいオプションの目的を理解できるようにするためのものです。このドキュメントの更新は、機能追加に伴う標準的な手順です。 -
src/cmd/ld/lib.c
のlibinit
関数の変更:libinit
関数は、リンカの初期化処理を行う部分です。ここで、debug['b']
(リンカに-b
フラグが渡されたかどうかを示す内部フラグ)がチェックされます。もし-b
フラグが設定されていれば、race
変数が"_race"
に設定されます。このrace
変数は、smprint
関数を使ってライブラリパスを構築する際に使用されます。結果として、リンカは通常の$GOROOT/pkg/goos_goarch
ディレクトリではなく、$GOROOT/pkg/goos_goarch_race
のようなパスからライブラリを探すようになります。これは、Race Detectorが有効なビルドでは、通常のライブラリとは異なる、インストゥルメンテーションされたバージョンのライブラリが必要となるためです。 -
src/cmd/ld/lib.c
のloadlib
関数の変更:loadlib
関数は、リンカが内部的に必要なパッケージをロードする部分です。ここでもdebug['b']
がチェックされ、もし-b
フラグが設定されていれば、loadinternal("runtime/race")
が呼び出されます。これは、runtime/race
パッケージを明示的にロードし、最終的な実行可能ファイルに含めることを保証します。runtime/race
パッケージは、データ競合検出のロジックと関連するデータ構造を含んでおり、これがなければRace Detectorは機能しません。
これらの変更は、Goのビルドシステムがデータ競合検出機能を透過的にサポートするための重要なステップであり、ユーザーは単に-race
フラグ(このコミット時点では-b
)をビルドコマンドに追加するだけで、この強力なデバッグ機能を利用できるようになります。
関連リンク
-
元の変更セット (CL 6456044): https://golang.org/cl/6456044 このコミットメッセージで言及されている、データ競合検出機能を追加する大規模な変更の最初の部分です。
-
このコミットの変更セット (CL 6488074): https://golang.org/cl/6488074 このコミット自体に対応するGoの変更リスト(Change List)です。
-
Go Race Detectorの公式ドキュメント: https://go.dev/doc/articles/race_detector Go言語の公式ドキュメントにおけるRace Detectorに関する詳細な解説です。このコミットの背景にある機能について深く理解するために非常に役立ちます。
参考にした情報源リンク
- Go Race Detectorの公式ドキュメント: https://go.dev/doc/articles/race_detector
- Go言語のリンカに関する一般的な情報: Go言語のリンカの内部動作に関する公式ドキュメントやブログ記事は限られていますが、Goのソースコード自体が最も正確な情報源となります。
- Goのビルドプロセスに関する情報:
Goのビルドプロセス、特に
go build
コマンドがどのように動作し、リンカがどのように呼び出されるかについての一般的な理解は、このコミットの変更を理解する上で役立ちます。