[インデックス 18692] ファイルの概要
このコミットは、Go言語のツールチェインの一部である cmd/dist
における buildruntime.c
ファイルの変更に関するものです。具体的には、Google Native Client (NaCl) 環境向けのビルドプロセスにおいて、不足していた定義を追加し、コンパイル時の挙動を微調整しています。
コミット
commit b3e0a8df24e5c98f516e542681bb9a752999dc29
Author: Dave Cheney <dave@cheney.net>
Date: Fri Feb 28 21:45:12 2014 +1100
cmd/dist: add missing Native Client bits
Some parts of CL 15400047 didn't survive the merge.
LGTM=rsc
R=rsc, iant
CC=golang-codereviews
https://golang.org/cl/69870044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b3e0a8df24e5c98f516e542681bb9a752999dc29
元コミット内容
このコミットは、以前の変更セット (CL 15400047) のマージ時に失われた、Native Client (NaCl) 関連の定義を cmd/dist
に追加することを目的としています。
変更の背景
Go言語は、様々なアーキテクチャやオペレーティングシステムをサポートするように設計されています。その中には、Googleが開発したサンドボックス技術であるNative Client (NaCl) も含まれていました。NaClは、ウェブブラウザ内でネイティブコードを安全に実行するための技術であり、Go言語もNaCl環境で動作するように対応が進められていました。
このコミットの背景には、以前の変更 (CL 15400047) が cmd/dist
のビルドプロセスに適切にマージされず、NaCl環境でGoランタイムをビルドするために必要な特定の定義が欠落していたという問題があります。cmd/dist
はGoのビルドシステムの中核をなすツールであり、Goのソースコードからコンパイラ、リンカ、アセンブラなどのツールチェインを構築する役割を担っています。buildruntime.c
はその中で、Goランタイムのビルドに必要なアセンブリコードの生成や、特定のプラットフォーム向けの定義を行う部分です。
この欠落により、NaCl環境向けのGoランタイムのビルドが正しく行えない、あるいは最適化が不十分になる可能性がありました。本コミットは、この問題を修正し、NaCl環境におけるGoのサポートを完全なものにすることを目的としています。
前提知識の解説
Google Native Client (NaCl)
Google Native Client (NaCl) は、ウェブブラウザ内でネイティブコードを安全かつポータブルに実行するためのオープンソース技術です。これは、C/C++などの言語で書かれたアプリケーションを、ブラウザのサンドボックス内で実行できるようにすることで、ウェブアプリケーションのパフォーマンスと機能を向上させることを目指していました。NaClは、特定のCPUアーキテクチャ(x86-32、x86-64、ARM)向けにコンパイルされたコードを、検証済みのサンドボックス内で実行します。セキュリティのために、コードは実行前に厳密な検証プロセスを経ます。
Go言語のビルドシステム (cmd/dist
)
Go言語のビルドシステムは、cmd/dist
というツールを中心に構築されています。cmd/dist
は、GoのソースコードからGoコンパイラ (gc
)、Goアセンブラ (go tool asm
)、Goリンカ (go tool link
) などのGoツールチェイン全体をビルドするために使用されます。これは、Goの自己ホスト型コンパイラが、それ自身をビルドするために必要なツールを生成するというブートストラッププロセスの一部です。
cmd/dist
は、Goのソースツリー内の様々なディレクトリにあるC言語のソースファイル(例: src/cmd/dist/buildruntime.c
)やGo言語のソースファイルなどをコンパイルし、最終的な実行可能ファイルを生成します。このプロセスでは、ターゲットとなるOSやアーキテクチャに応じて、異なるコンパイルオプションや定義が適用されます。
buildruntime.c
src/cmd/dist/buildruntime.c
は、Goランタイムのビルドプロセスにおいて、特にアセンブリコードの生成に関連する部分を担当するC言語のファイルです。このファイルは、Goランタイムが使用する低レベルのアセンブリルーチン(例えば、スレッドローカルストレージ (TLS) へのアクセスや、ゴルーチン構造体 (g
) やM構造体 (m
) へのアクセス)のためのマクロ定義を生成します。これらのマクロは、異なるOSやアーキテクチャの組み合わせに応じて、適切なアセンブリ命令に展開されるように定義されます。
TLS (Thread Local Storage)
TLS (Thread Local Storage) は、各スレッドが独自のデータコピーを持つことを可能にするメカニズムです。Go言語のランタイムでは、ゴルーチン (g
) やM (m
、OSスレッドを表す) といった重要なランタイム構造体へのポインタを効率的に取得するためにTLSが利用されることがあります。get_tls(r)
マクロは、このTLSポインタをレジスタ r
にロードするための命令を定義します。
g
と m
マクロ
Goランタイムでは、ゴルーチンは g
構造体で表現され、OSスレッドは m
構造体で表現されます。これらの構造体は、ランタイムの内部で頻繁にアクセスされるため、効率的なアクセス方法が求められます。g(r)
と m(r)
マクロは、TLSレジスタ r
を基準として、現在のゴルーチン (g
) と現在のM (m
) 構造体へのポインタを取得するためのオフセットを定義します。これらのオフセットは、アーキテクチャやOSによって異なる場合があります。
6c
コンパイラ
6c
は、Go言語の初期のツールチェインで使用されていたCコンパイラの一つです。Goのツールチェインは、Go言語自体で書かれたコンポーネントが増えるにつれて、徐々に自己ホスト型に移行していきましたが、初期のブートストラッププロセスや一部の低レベルな部分ではC言語が使用され、6c
のようなコンパイラが利用されていました。このコミットの時点では、6c
はまだGoのビルドプロセスの一部として使用されていました。
技術的詳細
このコミットは、src/cmd/dist/buildruntime.c
ファイルに以下の2つの主要な変更を加えています。
-
Native Client (NaCl) 向けのTLS/g/mマクロ定義の追加:
-
{"386", "nacl", ...}
エントリが追加されました。これは、32ビットx86アーキテクチャ (386) とNaCl環境の組み合わせに対する定義です。{"386", "nacl", // Same as Linux above. "#define\tget_tls(r)\tMOVL 8(GS), r\\n" "#define\tg(r)\t-8(r)(GS*1)\\n" "#define\tm(r)\t-4(r)(GS*1)\\n" },
この定義は、Linux 386環境と類似しており、
GS
セグメントレジスタを介してTLSにアクセスし、そこからg
とm
構造体へのオフセットを計算しています。MOVL 8(GS), r
は、GS
レジスタが指すアドレスから8バイトオフセットした位置にある値をレジスタr
にロードする命令です。これは、TLSベースポインタを取得する一般的な方法です。-8(r)(GS*1)
や-4(r)(GS*1)
は、TLSベースポインタからの相対オフセットでg
とm
を参照することを示しています。 -
{"amd64p32", "nacl", ...}
エントリが追加されました。これは、64ビットx86アーキテクチャ (amd64) の32ビットポインタモード (amd64p32
) とNaCl環境の組み合わせに対する定義です。{"amd64p32", "nacl", "#define get_tls(r)\\n" "#define g(r) 0(GS)\\n" "#define m(r) 4(GS)\\n" },
amd64p32
は、64ビットアーキテクチャ上で32ビットポインタを使用するモードを指します。この場合、get_tls(r)
は空のマクロとして定義されており、TLSポインタの取得が不要か、別のメカニズムで行われることを示唆しています。g(r)
とm(r)
は、GS
レジスタからの直接オフセットでg
とm
を参照しています。これは、TLSのベースアドレスがGS
レジスタに直接格納されている場合に典型的なパターンです。
-
-
mkzasm
関数における6c
コンパイルオプションの調整:mkzasm
関数内で6c
コンパイラを呼び出す際のオプションの順序が変更されました。 変更前:// to get acid [sic] output. vreset(&argv); vadd(&argv, bpathf(&b, "%s/%sc", tooldir, gochar)); vadd(&argv, "-D"); vadd(&argv, bprintf(&b, "GOOS_%s", goos)); vadd(&argv, "-D"); vadd(&argv, bprintf(&b, "GOARCH_%s", goarch)); vadd(&argv, "-I"); vadd(&argv, bprintf(&b, "%s", workdir)); vadd(&argv, "-a"); // ここにあった vadd(&argv, "-n"); vadd(&argv, "-o"); vadd(&argv, bpathf(&b, "%s/proc.acid", workdir)); vadd(&argv, "proc.c");
変更後:
// to get acid [sic] output. Run once without the -a -o workdir/proc.acid in order to // report compilation failures (the -o redirects all messages, unfortunately). vreset(&argv); vadd(&argv, bpathf(&b, "%s/%sc", tooldir, gochar)); vadd(&argv, "-D"); vadd(&argv, bprintf(&b, "GOOS_%s", goos)); vadd(&argv, "-D"); vadd(&argv, bprintf(&b, "GOARCH_%s", goarch)); vadd(&argv, "-I"); vadd(&argv, bprintf(&b, "%s", workdir)); // vadd(&argv, "-a"); // ここから削除 vadd(&argv, "-n"); vadd(&argv, "-a"); // ここに移動 vadd(&argv, "-o"); vadd(&argv, bpathf(&b, "%s/proc.acid", workdir)); vadd(&argv, "proc.c");
コメントが追加されており、「コンパイルエラーを報告するために、
-a -o workdir/proc.acid
なしで一度実行する」という意図が示されています。-o
オプションは通常、コンパイラの出力を指定されたファイルにリダイレクトするため、エラーメッセージもそのファイルに書き込まれてしまい、標準出力に表示されない問題がありました。この変更は、-a
(アセンブリ出力を生成) と-o
(出力ファイル指定) オプションを、エラー報告のフェーズの後、またはエラー報告に影響を与えないように配置し直すことで、コンパイルエラーが適切に表示されるようにするためのものです。
これらの変更は、GoのビルドシステムがNaCl環境をより適切にサポートし、ビルド時のデバッグ体験を向上させるために重要です。
コアとなるコードの変更箇所
src/cmd/dist/buildruntime.c
ファイルにおいて、以下の変更が行われました。
static struct { ... }
配列に、{"386", "nacl", ...}
と{"amd64p32", "nacl", ...}
の2つのエントリが追加されました。mkzasm
関数内で、vadd(&argv, "-a");
の行がvadd(&argv, "-n");
の後、vadd(&argv, "-o");
の前に移動しました。
コアとなるコードの解説
TLS/g/mマクロ定義の追加
buildruntime.c
のこのセクションは、Goランタイムが使用する低レベルのアセンブリマクロを定義するためのテーブルです。各エントリは、特定のアーキテクチャとOSの組み合わせ (GOARCH
, GOOS
) に対して、get_tls
, g
, m
の各マクロがどのように定義されるかを指定します。
-
{"386", "nacl", ...}
:get_tls(r)
:MOVL 8(GS), r
は、GS
セグメントレジスタを基準としたオフセット8
から値を読み込み、それをレジスタr
に格納します。これは、32ビットx86アーキテクチャにおけるTLSベースポインタの取得方法として一般的です。NaCl環境では、TLSの管理がホストシステムとは異なるため、このような明示的な定義が必要になります。g(r)
:-8(r)(GS*1)
は、TLSベースポインタ (r
に格納されている値) から-8
のオフセットにあるメモリ位置を参照します。これは、現在のゴルーチン (g
) 構造体へのポインタが格納されている場所を示します。m(r)
:-4(r)(GS*1)
は、TLSベースポインタ (r
に格納されている値) から-4
のオフセットにあるメモリ位置を参照します。これは、現在のM (m
) 構造体へのポインタが格納されている場所を示します。
-
{"amd64p32", "nacl", ...}
:get_tls(r)
: 空のマクロです。これは、amd64p32
(64ビットアーキテクチャ上の32ビットポインタモード) のNaCl環境では、TLSポインタの取得方法が異なるか、あるいはGS
レジスタ自体がTLSベースポインタを直接保持しているため、明示的なロード命令が不要であることを示唆しています。g(r)
:0(GS)
は、GS
セグメントレジスタが指すアドレスそのものを参照します。これは、GS
が直接g
構造体へのポインタを保持している場合に用いられます。m(r)
:4(GS)
は、GS
セグメントレジスタが指すアドレスから4
のオフセットにあるメモリ位置を参照します。これは、m
構造体へのポインタが格納されている場所を示します。
これらの定義は、GoランタイムがNaCl環境で正しく動作し、ゴルーチンやM構造体といった重要なランタイムデータに効率的にアクセスできるようにするために不可欠です。
mkzasm
関数における 6c
コンパイルオプションの調整
mkzasm
関数は、Goランタイムのアセンブリコードを生成するために 6c
コンパイラを呼び出す役割を担っています。この変更は、6c
コンパイラに渡す引数の順序を調整することで、コンパイルエラーの報告を改善することを目的としています。
-a
オプションの移動:-a
オプションは、6c
コンパイラにアセンブリ出力を生成するように指示します。-o
オプションは、コンパイラの出力を指定されたファイルにリダイレクトします。- 元の順序では、
-a
が-o
の前にあったため、コンパイルエラーメッセージもproc.acid
ファイルにリダイレクトされてしまい、標準出力に表示されませんでした。 - 新しい順序では、
-a
を-o
の直前に移動させることで、コンパイラがエラーメッセージを標準出力に書き出す機会を確保しつつ、最終的なアセンブリ出力をproc.acid
にリダイレクトできるようにしています。 - 追加されたコメント
Run once without the -a -o workdir/proc.acid in order to report compilation failures (the -o redirects all messages, unfortunately).
は、この変更の意図を明確にしています。つまり、まず-a
と-o
なしでコンパイルを試み、エラーがあればそれを報告し、その後でアセンブリ出力を生成するという、より堅牢なビルドプロセスを意図している可能性があります。
この変更は、開発者がGoランタイムのビルド時に発生するコンパイルエラーをより容易に特定し、デバッグできるようにするための、ビルドシステムの使いやすさの改善に貢献しています。
関連リンク
- Go言語のビルドシステムに関するドキュメント (Goの公式ドキュメントやブログ記事を参照)
- Google Native Client (NaCl) の公式ドキュメント (現在は開発が終了しているため、アーカイブされた情報源を参照)
- Goランタイムのソースコード (特に
src/runtime
ディレクトリ)
参考にした情報源リンク
- Go言語の公式ドキュメント: https://go.dev/doc/
- Go言語のソースコードリポジトリ: https://github.com/golang/go
- Google Native Client (NaCl) の情報 (例: Wikipedia, 過去のGoogle Developersブログ記事など)
- Goのツールチェインに関する技術ブログや記事 (例: Dave Cheney氏のブログなど)
(注: NaClは現在開発が終了しており、Go言語の最新バージョンではサポートされていません。このコミットは、GoがNaClを積極的にサポートしていた時期のものです。)