[インデックス 18665] ファイルの概要
このコミットは、GoコンパイラおよびアセンブラがGoogle Native Client (NaCl) をサポートするための変更を導入しています。具体的には、GOARCH環境変数を参照するようにビルドツールを更新し、NaCl環境における特定のアーキテクチャ(amd64)でのレジスタ使用制限やアドレッシングモードの制約に対応しています。
コミット
commit 7954b2b90be1b424e1dc8d9d50b3edd1d7a60d4c
Author: Dave Cheney <dave@cheney.net>
Date: Thu Feb 27 06:57:06 2014 +1100
cmd/5a, cmd/5c, cmd/6a, cmd/6c, cmd/8a, cmd/8c, cmd/cc: support for Native Client
From the original description in CL 15770043
The main change here is to consult $GOARCH.
In 6c, when GOOS=nacl, some of the more complex addressing modes must be disabled, and the BP and R15 registers must not be used.
See golang.org/s/go13nacl for design overview.
LGTM=rsc
R=golang-codereviews, gobot, rsc
CC=golang-codereviews
https://golang.org/cl/69020044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/7954b2b90be1b424e1dc8d9d50b3edd1d7a60d4c
元コミット内容
このコミットは、Goコンパイラ (cmd/5c, cmd/6c, cmd/8c) とアセンブラ (cmd/5a, cmd/6a, cmd/8a)、および共通のCコンパイラフロントエンド (cmd/cc) に、Google Native Client (NaCl) のサポートを追加することを目的としています。主な変更点は、ビルドプロセスにおいて$GOARCH環境変数を参照するようにしたことです。特に、GOOS=naclの場合、6c (amd64アーキテクチャのGoコンパイラ) において、一部の複雑なアドレッシングモードが無効化され、BPおよびR15レジスタの使用が禁止されるようになります。
変更の背景
Go言語は、様々なプラットフォームやアーキテクチャで動作するように設計されています。Google Native Client (NaCl) は、ウェブブラウザ内で安全にネイティブコードを実行するための技術であり、GoプログラムをNaCl環境で実行できるようにすることは、Goの適用範囲を広げる上で重要でした。
この変更の背景には、Go 1.3におけるNaClサポートの取り組みがあります。NaCl環境は、セキュリティ上の理由から、通常のネイティブ環境とは異なる制約を課します。例えば、特定のシステムコールが利用できなかったり、メモリ保護の仕組みが異なったりします。特に、レジスタの使用やアドレッシングモードに関して、NaClのサンドボックス環境に適合させるための調整が必要でした。
このコミットは、GoコンパイラとアセンブラがNaClの制約を理解し、それに準拠したコードを生成できるようにするための基盤を構築するものです。これにより、GoプログラムがNaCl環境で安全かつ効率的に動作することが可能になります。
前提知識の解説
GoのビルドプロセスとGOOS/GOARCH
Go言語のビルドプロセスは、クロスコンパイルを強力にサポートしています。これは、GOOSとGOARCHという2つの環境変数によって制御されます。
GOOS(Go Operating System): ターゲットとなるオペレーティングシステムを指定します。例えば、linux、windows、darwin(macOS)、freebsdなどがあります。このコミットでは、naclという新しいGOOSが導入され、Google Native Client環境をターゲットとすることを示します。GOARCH(Go Architecture): ターゲットとなるCPUアーキテクチャを指定します。例えば、amd64、386、armなどがあります。
Goのビルドツールチェーンは、これらの環境変数の値に基づいて、適切なコンパイラ、アセンブラ、リンカを選択し、ターゲット環境に特化したバイナリを生成します。
Google Native Client (NaCl)
Google Native Client (NaCl) は、ウェブブラウザ内でC/C++などのネイティブコードを安全かつポータブルに実行するための技術です。NaClは、サンドボックス環境を提供することで、悪意のあるコードがシステムにアクセスするのを防ぎます。
NaClの主な特徴は以下の通りです。
- サンドボックス化: 実行されるコードは厳重に隔離された環境で動作し、ファイルシステムやネットワークなどのシステムリソースへの直接アクセスが制限されます。
- ポータビリティ: NaClバイナリは、異なるCPUアーキテクチャ(x86-32、x86-64、ARM)間で互換性を持つように設計されています。これは、Portable Native Client (PNaCl) と呼ばれる中間表現を介して実現されます。
- セキュリティ: コードの検証、メモリ保護、システムコールフィルタリングなどのメカニズムにより、セキュリティが強化されています。
NaCl環境では、通常のOS環境とは異なる制約が課せられるため、コンパイラやアセンブラはこれらの制約を考慮してコードを生成する必要があります。特に、特定のレジスタの使用が禁止されたり、アドレッシングモードに制限があったりすることがあります。
Goのコンパイラとアセンブラ
Goのツールチェーンには、各アーキテクチャに対応するコンパイラとアセンブラが含まれています。
cmd/5a,cmd/6a,cmd/8a: それぞれARM (5a), AMD64 (6a), x86 (8a) アーキテクチャ用のアセンブラです。アセンブラは、アセンブリ言語のソースコードを機械語に変換します。cmd/5c,cmd/6c,cmd/8c: それぞれARM (5c), AMD64 (6c), x86 (8c) アーキテクチャ用のGoコンパイラです。これらのコンパイラは、Goのソースコードをアセンブリ言語に変換し、その後アセンブラによって機械語に変換されます。cmd/cc: Goのコンパイラが内部的に使用するCコンパイラのフロントエンドです。Goのコンパイラは、一部の低レベルな処理やランタイムのコードをC言語で記述しており、それをコンパイルするためにcmd/ccを使用します。
これらのツールは、GOOSとGOARCHの値に基づいて、ターゲット環境に合わせたコードを生成する役割を担っています。
技術的詳細
このコミットにおける技術的な変更は、主に以下の点に集約されます。
-
GOARCHの参照:src/cmd/*/lex.c(アセンブラの字句解析部分) およびsrc/cmd/*/swt.c(コンパイラのスイッチ文処理部分) において、生成されるオブジェクトファイルのヘッダにgetgoarch()の値を埋め込むように変更されました。これにより、オブジェクトファイルがどのアーキテクチャ向けにビルドされたかを明確に識別できるようになります。src/cmd/5a/lex.c,src/cmd/6a/lex.c,src/cmd/8a/lex.cおよびsrc/cmd/cc/lex.cでは、getgoarch()で取得した値が、現在のコンパイラ/アセンブラがサポートするアーキテクチャ (thestring) と一致するかどうかを厳密にチェックするようになりました。これにより、誤ったGOARCH設定でのビルドを防ぎます。
-
NaCl固有の制約対応:
naclフラグの導入:src/cmd/cc/cc.hにnaclというグローバル変数が追加され、src/cmd/cc/lex.cでgetgoos()が"nacl"を返す場合にnaclがtrueに設定されるようになりました。このフラグは、NaCl環境特有の挙動を制御するために使用されます。flag_largemodelの設定:src/cmd/cc/lex.cでnaclがtrueの場合、flag_largemodelが1に設定されます。これは、NaCl環境ではアドレス空間が広いため、ポインタのサイズが通常よりも大きくなることを示唆している可能性があります。- レジスタ使用の制限 (amd64/6c):
src/cmd/6c/reg.cのBtoR関数において、naclがtrueの場合、BPレジスタ (D_BP) とR15レジスタ (D_R15) の使用が禁止されるようになりました。これは、NaClのサンドボックス環境がこれらのレジスタに特別な意味を持たせているため、Goコンパイラがこれらを通常の汎用レジスタとして使用することを避ける必要があるためです。src/cmd/6c/txt.cのginit関数とgclean関数において、naclがtrueの場合、BPとR15レジスタが予約済みとして扱われ、コンパイラがこれらを割り当てないように制御されます。
- アドレッシングモードの制限 (amd64/6c):
src/cmd/6c/sgen.cのxcom関数において、naclがtrueの場合、一部の複雑なアドレッシングモード(n->addable == 8の条件)が無効化されるようになりました。これは、NaClがサポートするアドレッシングモードが通常のx86-64アーキテクチャよりも制限されているためです。
- ポインタサイズの調整 (amd64/6c):
src/cmd/6c/txt.cにおいて、ewidth[TIND](ポインタの幅) が4バイトの場合の特殊な処理が追加されました。これは、NaClの32ビット環境 (amd64p32) をサポートするためです。amd64p32は、64ビットアーキテクチャ上で32ビットポインタを使用するモードで、メモリ使用量を削減する目的で利用されます。gmove関数やgopcode関数など、ポインタの型 (TIND) を扱う箇所で、ewidth[TIND] == 4の場合にTUINT(符号なし整数) として扱うなどの調整が行われています。これにより、ポインタのサイズが4バイトであることをコンパイラが正しく認識し、適切な命令を生成できるようになります。exreg関数では、外部レジスタのオフセット計算にewidth[TIND]を使用するようになり、ポインタサイズに応じたオフセットが確保されるようになりました。
- アラインメントの調整 (amd64/6c):
src/cmd/6c/swt.cのalign関数において、ewidth[TIND] == 4の場合の引数アラインメントのロジックが追加されました。これにより、NaClの32ビットポインタ環境での関数呼び出し規約に合わせたアラインメントが保証されます。
-
thecharとthestringの初期化位置の変更:src/cmd/5c/txt.c,src/cmd/6c/txt.c,src/cmd/8c/txt.cにおいて、thecharとthestringの初期化がginit()関数内からグローバル変数宣言時に変更されました。これにより、これらの変数がより早期に利用可能になり、main()関数でのGOARCHチェックに利用できるようになります。
これらの変更により、Goのコンパイラとアセンブラは、NaCl環境の特殊な要件に対応し、GoプログラムがNaClサンドボックス内で正しく動作するためのコードを生成できるようになりました。
コアとなるコードの変更箇所
このコミットで変更された主要なファイルと、その変更の概要は以下の通りです。
src/cmd/5a/lex.c:main関数でGOARCHをチェックし、現在のアーキテクチャ (thestring) と一致しない場合はエラーを出すように変更。- オブジェクトファイルのヘッダに
getgoarch()の値を埋め込むように変更。
src/cmd/5c/swt.c:- オブジェクトファイルのヘッダに
getgoarch()の値を埋め込むように変更。
- オブジェクトファイルのヘッダに
src/cmd/5c/txt.c:thecharとthestringの初期化をグローバル変数宣言時に移動。
src/cmd/6a/lex.c:main関数でGOARCHをチェックし、現在のアーキテクチャ (thestring) と一致しない場合はエラーを出すように変更。- オブジェクトファイルのヘッダに
getgoarch()の値を埋め込むように変更。
src/cmd/6c/reg.c:BtoR関数でnaclがtrueの場合、BPとR15レジスタの使用を禁止するロジックを追加。
src/cmd/6c/sgen.c:xcom関数でnaclがtrueの場合、一部の複雑なアドレッシングモードを無効化する条件を追加。
src/cmd/6c/swt.c:- オブジェクトファイルのヘッダに
getgoarch()の値を埋め込むように変更。 align関数でewidth[TIND] == 4の場合の引数アラインメントのロジックを追加。
- オブジェクトファイルのヘッダに
src/cmd/6c/txt.c:thecharとthestringの初期化をグローバル変数宣言時に移動。_64BITREGを定義し、ewidth[TIND] == 8の場合に_64BITを定義するように変更。ginit関数とgclean関数でnaclがtrueの場合、BPとR15レジスタを予約済みとして扱うロジックを追加。naddr,gmove,gopcode,exreg関数でewidth[TIND]の値に基づいてポインタの型やオフセット計算を調整。
src/cmd/8a/lex.c:main関数でGOARCHをチェックし、現在のアーキテクチャ (thestring) と一致しない場合はエラーを出すように変更。- オブジェクトファイルのヘッダに
getgoarch()の値を埋め込むように変更。
src/cmd/8c/swt.c:- オブジェクトファイルのヘッダに
getgoarch()の値を埋め込むように変更。
- オブジェクトファイルのヘッダに
src/cmd/8c/txt.c:thecharとthestringの初期化をグローバル変数宣言時に移動。
src/cmd/cc/cc.h:naclという外部変数を追加。thecharとthestringの宣言をexternに変更。
src/cmd/cc/lex.c:main関数でGOARCHをチェックし、現在のアーキテクチャ (thestring) と一致しない場合はエラーを出すように変更。GOARCHが"amd64p32"の場合にewidth[TIND]を4に設定。getgoos()が"nacl"の場合にnaclフラグをtrueに設定し、flag_largemodelを1に設定。
コアとなるコードの解説
GOARCHの厳密なチェック
src/cmd/*/lex.c (アセンブラ) および src/cmd/cc/lex.c (Cコンパイラフロントエンド) のmain関数に以下のコードが追加されました。
p = getgoarch();
if(strncmp(p, thestring, strlen(thestring)) != 0)
sysfatal("cannot use %cc with GOARCH=%s", thechar, p);
これは、現在のビルドツール(アセンブラまたはCコンパイラ)が意図されたGOARCHと一致しているかを厳密にチェックするためのものです。thestringは、そのツールがサポートするアーキテクチャ(例: amd64, arm, 386)を表します。もしGOARCH環境変数の値がthestringと異なる場合、ビルドは中断され、エラーメッセージが表示されます。これにより、誤ったクロスコンパイル設定による問題を未然に防ぎます。
NaCl固有のレジスタ制限 (amd64/6c)
src/cmd/6c/reg.cのBtoR関数に以下の変更が加えられました。
b &= 0xffffL;
if(nacl)
b &= ~((1<<(D_BP-D_AX)) | (1<<(D_R15-D_AX)));
if(b == 0)
return 0;
return bitno(b) + D_AX;
BtoR関数は、レジスタのビットマスクからレジスタ番号を抽出する役割を担っています。naclフラグがtrueの場合、BPレジスタ (D_BP) とR15レジスタ (D_R15) に対応するビットがマスクからクリアされます。これは、NaCl環境ではこれらのレジスタが特別な用途に予約されており、Goコンパイラが通常の汎用レジスタとして使用することを避ける必要があるためです。
また、src/cmd/6c/txt.cのginit関数とgclean関数でも、naclがtrueの場合にこれらのレジスタが予約済みとして扱われるようになり、コンパイラがレジスタ割り当てを行う際にこれらを避けるようになります。
NaCl固有のアドレッシングモード制限 (amd64/6c)
src/cmd/6c/sgen.cのxcom関数に以下の変更が加えられました。
if(n->addable == 8 && !side(n) && !nacl) {
indx(n);
l = new1(OINDEX, idx.basetree, idx.regtree);
l->scale = idx.scale;
このコードは、特定の複雑なアドレッシングモード (n->addable == 8) を使用できるかどうかを判断する部分です。!naclという条件が追加されたことで、naclフラグがtrueの場合、このアドレッシングモードは無効化されます。これは、NaClのサンドボックス環境がサポートするアドレッシングモードが通常のx86-64アーキテクチャよりも制限されているため、GoコンパイラがNaCl互換のコードを生成するために必要な調整です。
ポインタサイズの動的な調整 (amd64/6c)
src/cmd/cc/lex.cに以下のコードが追加されました。
if(strcmp(getgoarch(), "amd64p32") == 0) // must be before cinit
ewidth[TIND] = 4;
このコードは、GOARCHが"amd64p32"(64ビットアーキテクチャ上で32ビットポインタを使用するモード)の場合に、ポインタのサイズ (ewidth[TIND]) を4バイトに設定します。これにより、コンパイラはポインタを32ビットとして扱い、それに応じた命令を生成します。
また、src/cmd/6c/txt.cのgmove関数やgopcode関数など、ポインタの型 (TIND) を扱う多くの箇所で、ewidth[TIND]の値に基づいてポインタの型をTUINT (符号なし整数) として扱うなどの調整が行われています。これにより、ポインタのサイズが4バイトであることをコンパイラが正しく認識し、適切なロード/ストア命令(例: AMOVLではなくAMOVQ)を生成できるようになります。
これらの変更は、GoコンパイラがNaCl環境の特定の制約(レジスタ使用、アドレッシングモード、ポインタサイズ)を考慮し、それに準拠した効率的で安全なコードを生成できるようにするために不可欠です。
関連リンク
- Go CL 69020044: https://golang.org/cl/69020044
- Go 1.3 Native Client Design Overview: golang.org/s/go13nacl
参考にした情報源リンク
- Reddit discussion about Go 1.3 Native Client Support: https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQG5qVrFnCkdtsI1ct2sj86aeKYCDCffiIXZZkE54QjsJ8M0tB1uO8XT1OmIh2yf5-mxRSBdpi1_WTIa-BYEqhMCn9LuV6fctQpAdl3J5jAjOy_dQWut1POsq1v6-SZZ_yy2EqW78M9Z0PAvr4DMMQ11KgEUFr-v2XZnNUJ1EC2Nprz4ryns1To=
- Google Native Client (Wikipedia): https://en.wikipedia.org/wiki/Google_Native_Client (一般的なNaClの概念理解のため)
- Go Environment Variables (
GOOS,GOARCH): https://go.dev/doc/install/source#environment (一般的なGoの環境変数理解のため)