[インデックス 12151] ファイルの概要
このコミットは、Go言語のARMアーキテクチャ向けコンパイラ(5c
, 5g
)およびリンカ(5l
)におけるバグ修正を目的としています。具体的には、命令のフラグとして使用されるreg
フィールドが、デフォルト値のNREG
(16)を持つことで、誤ってNOPTR
ビット(これも値が16)と解釈されてしまう問題を解決します。これにより、ポインタ情報が正しく扱われず、ビルドや実行時に問題が発生する可能性がありました。このコミットでは、reg
フィールドを明示的に0にクリアすることで、この誤解釈を防ぎ、ARMビルドの安定性を向上させています。
コミット
- コミットハッシュ:
a5bc16d619657a243ea55c2ebefc9a2f672ab2de
- Author: Russ Cox rsc@golang.org
- Date: Wed Feb 22 16:29:14 2012 -0500
- コミットメッセージ:
5c, 5g, 5l: fix arm bug Using reg as the flag word was unfortunate, since the default value is not 0 but NREG (==16), which happens to be the bit NOPTR now. Clear it. If I say this will fix the build, it won't. R=golang-dev, r CC=golang-dev https://golang.org/cl/5690072
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/a5bc16d619657a243ea55c2ebefc9a2f672ab2de
元コミット内容
commit a5bc16d619657a243ea55c2ebefc9a2f672ab2de
Author: Russ Cox <rsc@golang.org>
Date: Wed Feb 22 16:29:14 2012 -0500
5c, 5g, 5l: fix arm bug
Using reg as the flag word was unfortunate, since the
default value is not 0 but NREG (==16), which happens
to be the bit NOPTR now. Clear it.
If I say this will fix the build, it won't.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5690072
---
src/cmd/5c/txt.c | 3 ++-
src/cmd/5g/gsubr.c | 4 +++-\
src/cmd/5g/list.c | 2 +-\
src/cmd/5l/list.c | 2 +-\
4 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/src/cmd/5c/txt.c b/src/cmd/5c/txt.c
index dea406eb92..1a189e3afe 100644
--- a/src/cmd/5c/txt.c
+++ b/src/cmd/5c/txt.c
@@ -1185,7 +1185,8 @@ gpseudo(int a, Sym *s, Node *n)\
if(a == ATEXT) {\
p->reg = textflag;\
textflag = 0;\
-\t}\
+\t} else if(a == AGLOBL)\
+\t\tp->reg = 0;\
if(s->class == CSTATIC)\
p->from.name = D_STATIC;\
naddr(n, &p->to);\
diff --git a/src/cmd/5g/gsubr.c b/src/cmd/5g/gsubr.c
index 86f05fb32c..9acf93670f 100644
--- a/src/cmd/5g/gsubr.c
+++ b/src/cmd/5g/gsubr.c
@@ -75,6 +75,7 @@ prog(int as)\
\tp = dpc;\
\tdpc = mal(sizeof(*dpc));\
\tp->link = dpc;\
+\t\tp->reg = 0; // used for flags\
} else {\
\tp = pc;\
\tpc = mal(sizeof(*pc));\
@@ -1116,7 +1117,8 @@ gins(int as, Node *f, Node *t)\
if(f != N)\
\tnaddr(f, &af, 1);\
if(t != N)\
-\t\tnaddr(t, &at, 1);\tp = prog(as);\
+\t\tnaddr(t, &at, 1);\
+\tp = prog(as);\
if(f != N)\
\tp->from = af;\
if(t != N)\
diff --git a/src/cmd/5g/list.c b/src/cmd/5g/list.c
index fef9c85435..9bc3a9a9a6 100644
--- a/src/cmd/5g/list.c
+++ b/src/cmd/5g/list.c
@@ -59,7 +59,7 @@ Pconv(Fmt *fp)\
switch(p->as) {\
default:\
\tsnprint(str1, sizeof(str1), "%A%C", p->as, p->scond);\
-\t\tif(p->reg == NREG)\
+\t\tif(p->reg == NREG && p->as != AGLOBL)\
\t\tsnprint(str, sizeof(str), "%.4d (%L) %-7s\t%D,%D", \
\t\t\tp->loc, p->lineno, str1, &p->from, &p->to);\
\telse\
diff --git a/src/cmd/5l/list.c b/src/cmd/5l/list.c
index fa838215b1..7b623d78a5 100644
--- a/src/cmd/5l/list.c
+++ b/src/cmd/5l/list.c
@@ -65,7 +65,7 @@ Pconv(Fmt *fp)\
switch(a) {\
default:\
\tfmtprint(fp, "(%d)", p->line);\
-\t\tif(p->reg == NREG)\
+\t\tif(p->reg == NREG && p->as != AGLOBL)\
\t\tfmtprint(fp, "\t%A%C\t%D,%D",\
\t\t\ta, p->scond, &p->from, &p->to);\
\telse\
変更の背景
この変更は、Go言語のARMアーキテクチャ向けコンパイラおよびリンカにおける、reg
フィールドの誤用によるバグを修正するために行われました。
Goコンパイラの内部では、命令やデータ構造を表現するために様々なフィールドが使用されます。その中には、レジスタ番号を示すreg
フィールドがありますが、このフィールドがレジスタ番号だけでなく、命令に関する特定のフラグ(例えば、ポインタ情報)を格納するためにも流用されていました。
問題は、reg
フィールドのデフォルト値が0ではなく、NREG
という定数(値は16)であったことです。偶然にも、ポインタ情報を示すNOPTR
というビットフラグも値が16でした。このため、reg
フィールドがデフォルト値のNREG
を持つ場合、それが意図せずNOPTR
フラグが設定されていると解釈されてしまい、ポインタの追跡やガベージコレクションに誤った情報が渡される可能性がありました。
この誤解釈は、特にARMアーキテクチャのビルドにおいて、正しくないコード生成やランタイムエラーを引き起こす原因となっていました。コミットメッセージにある「If I say this will fix the build, it won't.」という一文は、このバグがビルドプロセス全体に与える影響の複雑さを示唆している可能性があります。
前提知識の解説
Go言語のコンパイラツールチェイン (5c
, 5g
, 5l
)
Go言語の初期のコンパイラツールチェインは、Plan 9オペレーティングシステムのツールチェインに由来しています。これらのツールは、特定のアーキテクチャをターゲットとする際に、数字と文字の組み合わせで命名されていました。
5c
: ARMアーキテクチャ向けのCコンパイラ。Go言語のランタイムや一部のライブラリはC言語で書かれていたため、これらをコンパイルするために使用されました。5g
: ARMアーキテクチャ向けのGoコンパイラ。Go言語のソースコードをコンパイルするために使用されました。5l
: ARMアーキテクチャ向けのリンカ。コンパイルされたオブジェクトファイルを結合して実行可能ファイルを生成するために使用されました。
数字の「5」はARMアーキテクチャを指し、同様に「6」はamd64
、「8」は386
を指していました。現代のGo開発では、go build
コマンドがこれらの個別のコンパイラやリンカの呼び出しを抽象化しているため、開発者が直接これらの名前を意識することは少なくなっています。
NREG
とNOPTR
Goコンパイラの内部では、命令やデータ構造の属性を表現するために様々な定数やフラグが定義されています。
NREG
: "No Register" (レジスタなし) を意味する定数で、特定の命令がレジスタを使用しない場合や、レジスタが割り当てられていない状態を示すために使用されます。このコミットの時点では、NREG
の値は16でした。NOPTR
: "No Pointer" (ポインタなし) を意味するビットフラグです。これは、特定のメモリ領域やデータ構造がポインタを含まないことを示すために使用されます。Goのガベージコレクタは、この情報を使用して、どのメモリ領域をスキャンする必要があるかを判断します。このコミットの時点では、NOPTR
ビットの値も16でした。
この二つの定数が偶然にも同じ値を持っていたことが、今回のバグの根本原因となりました。reg
フィールドがデフォルト値のNREG
を持つと、それがNOPTR
フラグが設定されていると誤解釈され、ガベージコレクタがポインタをスキャンすべきでない場所をスキップしてしまう可能性がありました。
技術的詳細
このバグは、Goコンパイラの内部表現におけるProg
構造体(またはそれに類する命令表現)のreg
フィールドのセマンティクスに起因していました。
通常、reg
フィールドは命令が使用するレジスタ番号を格納するために設計されています。しかし、Goコンパイラの設計では、このreg
フィールドがレジスタ番号だけでなく、命令に関する追加のメタデータやフラグを格納するためにも再利用されていました。これは、メモリ効率やコードの簡潔さを追求する上で一般的な最適化手法です。
問題は、このreg
フィールドが初期化されない場合、または特定の条件下でデフォルト値を持つ場合に発生しました。コミットメッセージによると、reg
フィールドのデフォルト値は0ではなく、NREG
(値は16)でした。
同時に、Goのランタイムとガベージコレクタは、メモリ内のポインタを正確に追跡するために、命令やデータ構造に付随するポインタ情報に依存しています。このポインタ情報は、NOPTR
のようなビットフラグとして表現されることがあり、このNOPTR
フラグも値が16でした。
したがって、reg
フィールドがデフォルト値のNREG
(16)を持つと、コンパイラやリンカの他の部分が、そのreg
フィールドを「ポインタを含まない」ことを示すNOPTR
フラグが設定されていると誤解釈してしまいました。これは、実際にはポインタを含む可能性のあるデータや命令に対して、ガベージコレクタが誤った動作をする原因となり、メモリリークやクラッシュなどの深刻な問題を引き起こす可能性がありました。
このコミットの解決策は、reg
フィールドがフラグとして使用される場合に、その値を明示的に0にクリアすることです。これにより、NREG
とNOPTR
の値が偶然一致することによる誤解釈を防ぎ、ポインタ情報が正しく伝達されるようになります。
コアとなるコードの変更箇所
このコミットでは、以下の4つのファイルが変更されています。
src/cmd/5c/txt.c
src/cmd/5g/gsubr.c
src/cmd/5g/list.c
src/cmd/5l/list.c
これらのファイルは、それぞれARMアーキテクチャ向けのCコンパイラ、Goコンパイラのサブモジュール、およびリンカの一部を構成しています。変更は主に、命令を生成またはリスト表示する際に、reg
フィールドの扱いを修正することに焦点を当てています。
コアとなるコードの解説
src/cmd/5c/txt.c
このファイルは、C言語のソースコードからARMアセンブリコードを生成する部分に関連しています。
--- a/src/cmd/5c/txt.c
+++ b/src/cmd/5c/txt.c
@@ -1185,7 +1185,8 @@ gpseudo(int a, Sym *s, Node *n)\
if(a == ATEXT) {\
p->reg = textflag;\
textflag = 0;\
-\t}\
+\t} else if(a == AGLOBL)\
+\t\tp->reg = 0;\
if(s->class == CSTATIC)\
p->from.name = D_STATIC;\
naddr(n, &p->to);\
gpseudo
関数は、擬似命令(ATEXT, AGLOBLなど)を処理します。
変更前はATEXT
の場合のみp->reg
をtextflag
で設定していましたが、AGLOBL
(グローバル変数宣言)の場合にp->reg
が初期化されないままでした。この変更により、AGLOBL
の場合にp->reg
を明示的に0に設定することで、NREG
が誤ってNOPTR
と解釈されるのを防ぎます。
src/cmd/5g/gsubr.c
このファイルは、Goコンパイラの汎用サブルーチンを含んでいます。
--- a/src/cmd/5g/gsubr.c
+++ b/src/cmd/5g/gsubr.c
@@ -75,6 +75,7 @@ prog(int as)\
\tp = dpc;\
\tdpc = mal(sizeof(*dpc));\
\tp->link = dpc;\
+\t\tp->reg = 0; // used for flags\
} else {\
\tp = pc;\
\tpc = mal(sizeof(*pc));\
@@ -1116,7 +1117,8 @@ gins(int as, Node *f, Node *t)\
if(f != N)\
\tnaddr(f, &af, 1);\
if(t != N)\
-\t\tnaddr(t, &at, 1);\tp = prog(as);\
+\t\tnaddr(t, &at, 1);\
+\tp = prog(as);\
if(f != N)\
\tp->from = af;\
if(t != N)\
prog
関数は新しい命令(Prog
構造体)を割り当てて初期化します。変更前はp->reg
が初期化されていませんでしたが、p->reg = 0; // used for flags
という行が追加され、命令が生成される際にreg
フィールドが明示的に0にクリアされるようになりました。これにより、reg
がフラグとして使用される場合に、意図しないNREG
の値が残ることを防ぎます。
gins
関数は命令を生成する関数ですが、変更は主にフォーマットの修正であり、機能的な変更はprog
関数への依存によるものです。
src/cmd/5g/list.c
このファイルは、Goコンパイラが生成するアセンブリコードをリスト表示する部分に関連しています。
--- a/src/cmd/5g/list.c
+++ b/src/cmd/5g/list.c
@@ -59,7 +59,7 @@ Pconv(Fmt *fp)\
switch(p->as) {\
default:\
\tsnprint(str1, sizeof(str1), "%A%C", p->as, p->scond);\
-\t\tif(p->reg == NREG)\
+\t\tif(p->reg == NREG && p->as != AGLOBL)\
\t\tsnprint(str, sizeof(str), "%.4d (%L) %-7s\t%D,%D", \
\t\t\tp->loc, p->lineno, str1, &p->from, &p->to);\
\telse\
Pconv
関数は、Prog
構造体を文字列に変換して表示します。変更前はp->reg == NREG
の場合に特定のフォーマットで表示していましたが、AGLOBL
命令の場合にp->reg
がNREG
であっても、それがレジスタとしてのNREG
ではなく、フラグとしてのNREG
(つまりNOPTR
)である可能性がありました。&& p->as != AGLOBL
という条件が追加されたことで、AGLOBL
命令の場合はp->reg
がNREG
であっても、レジスタとして扱わないように表示ロジックが修正されました。これは、AGLOBL
がレジスタを使用しない命令であるため、reg
フィールドがフラグとしてのみ意味を持つことを反映しています。
src/cmd/5l/list.c
このファイルは、Goリンカが生成するアセンブリコードをリスト表示する部分に関連しています。
--- a/src/cmd/5l/list.c
+++ b/src/cmd/5l/list.c
@@ -65,7 +65,7 @@ Pconv(Fmt *fp)\
switch(a) {\
default:\
\tfmtprint(fp, "(%d)", p->line);\
-\t\tif(p->reg == NREG)\
+\t\tif(p->reg == NREG && p->as != AGLOBL)\
\t\tfmtprint(fp, "\t%A%C\t%D,%D",\
\t\t\ta, p->scond, &p->from, &p->to);\
\telse\
src/cmd/5g/list.c
と同様に、リンカのリスト表示部分でもPconv
関数が修正されています。p->reg == NREG
の条件に&& p->as != AGLOBL
が追加され、AGLOBL
命令の表示が正しく行われるようになりました。これにより、リンカが命令を解釈する際にも、reg
フィールドの誤解釈を防ぎます。
これらの変更は、reg
フィールドがレジスタ番号とフラグの両方の意味を持つという設計上の課題に対し、フラグとしての使用時に明示的な初期化を行うことで、NREG
とNOPTR
の偶然の一致によるバグを根本的に解決しています。
関連リンク
- Go CL 5690072: https://golang.org/cl/5690072
参考にした情報源リンク
- Go言語のコンパイラツールチェインに関する情報:
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEabYmaQx60jL1Mz_g6s5shosXIDptY9dfJ2e32HOcixQeVx0tYegjcoQVTGOT2wMjxB1UCoWwn-iBpnal8DGgGDF_tzo3m-EfiTG4DINxa6iR6JA3P_qLErbkvwf2anNLJjshAwE4UMCmsguKdXQUKNtYCx3DfUG04Fpw9Zu_Q5KgpP_GIoeTTtk4-Fg==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGdnxbyHrCKLzivT1WhEZi3HovBL3wcCQwJ6IF0QB_7YijG7JsW1_sAOg8rB3jir1jTPdj4Ekb4wfFvWM2K1H551shWEQGKuiI46MwlVQu7sSR7bFLRtyofnoq6-Z9F
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHupJmKVfg1r2BrWdb7GVke44byN39cs5AkQnGXV3bePDOoPptw2hfEq-dxUBBJ07yUyqPAp7ekpSv4VV_X_6fhHYuUHt0oRd_eILKIlYr3ZioLc9JIp2dD7jxpsbav9gDpfJKIHenBfPoZuHz0sxHQ7oTXLAVVGcO8rtjkWDTy
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFhVDha6nUUPT1fhyQCsrvLDRiFCuhiiXpFU9klUegb9syEmqLulbIX3wUJiJd61PmwbhfC9Ul3i9cRRRituKehZ-Pgahp3WxUgpwVylg74U67cunx_lhCJi36-CqO23UPsOQ4zg9c7W-_SlREp8EbGAfyVUvdDi9MA4G8am8E1v1IsBSzYCLUs1sUqbm0Mv2fcm12PtKxauERqciU=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFSSCbUKkdjgRIMjysfbnpuHnw2_blfTOD0JpRcKTL8nN6MunoFadePfU2zC647u0LTUYwjTA9uHeZ-pAlgyKhMqEEMFmbZwKlMAu6OZbeedSf4XzF7Sg==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHX71xWuQ0HM8YPvwlVwcixzM_coFXaWHk-I1134wNwBsE4UFpgTfu5RJjewxlUvIifjLbUdBYiAz-S2FKOmd1CcvNZ5ecmwUTcY6nTNdY72CwVA==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFvCv6bfBp_mXhe6cL-TYcRk_8OdK298fVcr1RlEC0-QY9Y0S0chh4XcZ2sr674MH0QHVqGVQoIy7gaDEf9YZiD5rlDMXixVkqjR7Ltjs6lfQYD5w==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHp_16NoFBB_DRWMXH2D7IjpYUvt43DH6HF52WV71TnmTP_BXsNsyRMRjDgHjhHtgRk0O5ZG-oGg2bK2pPEeayYiSSXxBAC3NPID66KJcrvovzwY7hOsjwrFlFsSo00SNCRFiZxPvgguulpUdx7XVoV_jetv_aCym3cv4dl