[インデックス 16829] ファイルの概要
このコミットは、Go言語のランタイムとリンカにおいて、Func
構造体から不要なフィールドを削除する変更です。これにより、Goバイナリのサイズ削減と、シンボルテーブルの効率化が図られています。
コミット
commit c75884185332458242c03b17014bc3801977f684
Author: Russ Cox <rsc@golang.org>
Date: Fri Jul 19 18:52:35 2013 -0400
cmd/ld, runtime: remove unused fields from Func
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/11604043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c75884185332458242c03b17014bc3801977f684
元コミット内容
cmd/ld, runtime: remove unused fields from Func
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/11604043
変更の背景
この変更は、Go 1.2のシンボルテーブル(pclntab)の構造変更に関連しています。Go 1.2では、プログラムカウンタ(PC)と行番号、ファイル名、スタックポインタ(SP)などの情報をマッピングするための新しい形式が導入されました。これに伴い、以前のバージョンで使用されていたFunc
構造体の一部のフィールド(x1
, x2
, x3
)が不要になりました。これらのフィールドは、将来的な使用のために予約されていたり、古い情報を含んでいたりしましたが、新しいpclntabの設計により、その役割が置き換えられたため、削除することが可能になりました。
不要なフィールドを削除することで、Goバイナリのサイズを削減し、メモリ使用量を最適化することができます。また、シンボルテーブルの構造がよりシンプルになり、デバッグ情報の処理効率も向上します。
前提知識の解説
Go言語のシンボルテーブル (pclntab)
Go言語の実行可能ファイルには、デバッグ情報やプロファイリング情報を提供するための「PC-line table (pclntab)」と呼ばれるセクションが含まれています。このテーブルは、特定のプログラムカウンタ(PC、つまり実行中の命令のアドレス)がどのソースファイルのどの行に対応するか、どの関数に属するか、スタックフレームのサイズはどのくらいか、といった情報を提供します。
pclntab
は、主に以下の目的で使用されます。
- スタックトレースの生成: パニック発生時やデバッガが停止した際に、現在の実行パスを人間が読める形式で表示するため。
- プロファイリング: どのコード行がCPU時間を消費しているかを特定するため。
- デバッグ: デバッガがソースコードと実行中のバイナリを関連付けるため。
Func
構造体
Func
構造体は、Goのランタイム内部で個々のGo関数に関するメタデータを保持するために使用されます。この構造体はpclntab
の一部として格納され、関数のエントリポイント、名前、引数のサイズ、スタックフレーム情報、PC-lineテーブルへのオフセットなど、関数に関する重要な情報を含んでいます。
Go 1.2以前のFunc
構造体には、将来の拡張や互換性のために予約されたり、古い情報が残っていたりするフィールドが存在していました。これらのフィールドは、新しいpclntab
の設計によってその役割が不要になったため、削除の対象となりました。
Goリンカ (cmd/ld
)
Goリンカ(cmd/ld
)は、Goのソースコードをコンパイルして生成されたオブジェクトファイル(.o
ファイル)を結合し、最終的な実行可能ファイルを生成するツールです。リンカの役割の一つに、pclntab
を含む実行可能ファイルの構造を構築することがあります。Func
構造体のレイアウト変更は、リンカがpclntab
を生成するロジックに直接影響を与えます。
技術的詳細
このコミットの技術的な核心は、Func
構造体からx1
, x2
, x3
という3つのint32
フィールドを削除し、それに伴いpclntab
のレイアウトと、それを読み書きするコードを更新することです。
Func
構造体の変更 (src/pkg/runtime/runtime.h
)
変更前:
struct Func
{
uintptr entry; // start pc
int32 nameoff; // function name
// TODO: Remove these fields.
int32 args; // in/out args size
int32 x1; // locals size
int32 frame; // legacy frame size; use pcsp if possible
int32 x2;
int32 x3;
int32 pcsp;
int32 pcfile;
int32 pcln;
int32 nfuncdata;
int32 nfuncdataoff;
};
変更後:
struct Func
{
uintptr entry; // start pc
int32 nameoff; // function name
// TODO: Perhaps remove these fields.
int32 args; // in/out args size
int32 frame; // legacy frame size; use pcsp if possible
int32 pcsp;
int32 pcfile;
int32 pcln;
int32 nfuncdata;
int32 nfuncdataoff;
};
x1
, x2
, x3
フィールドが削除されています。コメントも// TODO: Remove these fields.
から// TODO: Perhaps remove these fields.
に変更されており、将来的にargs
やframe
も削除される可能性が示唆されています。
pclntab
のレイアウト変更とリンカの調整 (src/cmd/ld/lib.c
)
pclntab
は、Func
構造体の情報が連続して格納されるため、Func
構造体のサイズ変更はpclntab
全体のレイアウトに影響します。リンカは、このpclntab
を構築する際に、各フィールドのオフセットとサイズを正確に計算する必要があります。
変更前は、Func
構造体の固定サイズを計算する際に、削除されたフィールドの分も考慮されていました。
end = funcstart + PtrSize + 6*4 + 5*4 + npcdata*4 + nfuncdata*PtrSize;
6*4
は、args
, x1
, frame
, x2
, x3
、そしてもう一つ(おそらくnameoff
)のint32
フィールドの合計サイズを示していました。
変更後、x1
, x2
, x3
が削除されたため、int32
フィールドの数が3つ減り、3*4
に変更されています。
end = funcstart + PtrSize + 3*4 + 5*4 + npcdata*4 + nfuncdata*PtrSize;
また、リンカがpclntab
にFunc
構造体の情報を書き込む際にも、削除されたフィールドに対応する「Dead space」の書き込みが削除されています。
// Dead space. TODO: Delete (and update all parsers).
off = setuint32(ftab, off, 0);
と
// Dead space. TODO: Delete (and update all parsers).
off = setuint32(ftab, off, 0);
off = setuint32(ftab, off, 0);
これらの行が削除されたことで、リンカは不要なデータをpclntab
に書き込まなくなりました。
シンボルテーブル解析ライブラリの更新 (src/libmach/sym.c
と src/pkg/debug/gosym/pclntab.go
)
Func
構造体のレイアウトが変更されたため、Goバイナリのシンボルテーブルを読み取るライブラリも、新しいレイアウトに合わせてオフセットを調整する必要があります。
src/libmach/sym.c
では、Func
構造体内の各フィールドへのオフセットを定義するマクロが追加され、それらのマクロを使用してフィールドにアクセスするように変更されています。
#define FuncEntry (0)
#define FuncName (pcptrsize)
#define FuncArgs (pcptrsize+4)
#define FuncFrame (pcptrsize+2*4)
#define FuncPCSP (pcptrsize+3*4)
#define FuncPCFile (pcptrsize+4*4)
#define FuncPCLine (pcptrsize+5*4)
これにより、Func
構造体のレイアウト変更に柔軟に対応できるようになりました。以前はハードコードされたオフセット(例: f+pcptrsize+6*4
)が使用されていましたが、新しいマクロを使用することで、より読みやすく、保守しやすいコードになっています。
同様に、src/pkg/debug/gosym/pclntab.go
でも、Func
構造体内のlinetab
やfiletab
へのオフセット計算が変更されています。
変更前:
linetab := t.binary.Uint32(f[t.ptrsize+8*4:])
filetab := t.binary.Uint32(f[t.ptrsize+7*4:])
変更後:
linetab := t.binary.Uint32(f[t.ptrsize+5*4:])
filetab := t.binary.Uint32(f[t.ptrsize+4*4:])
これは、x1
, x2
, x3
の3つのint32
フィールド(合計12バイト)が削除されたことに対応しています。オフセットが3*4 = 12
バイト分減っています。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更は、以下のファイルに集中しています。
src/pkg/runtime/runtime.h
:Func
構造体からx1
,x2
,x3
フィールドが削除されました。src/cmd/ld/lib.c
: リンカがpclntab
を構築する際のFunc
構造体のサイズ計算と、不要な「Dead space」の書き込みが削除されました。src/libmach/sym.c
:Func
構造体内のフィールドへのオフセットを定義するマクロが追加され、それらを使用してフィールドにアクセスするように変更されました。src/pkg/debug/gosym/pclntab.go
:debug/gosym
パッケージがpclntab
を解析する際のFunc
構造体内のフィールドオフセット計算が更新されました。
コアとなるコードの解説
src/pkg/runtime/runtime.h
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -405,12 +405,9 @@ struct Func
uintptr entry; // start pc
int32 nameoff; // function name
- // TODO: Remove these fields.
+ // TODO: Perhaps remove these fields.
int32 args; // in/out args size
- int32 x1; // locals size
int32 frame; // legacy frame size; use pcsp if possible
- int32 x2;
- int32 x3;
int32 pcsp;
int32 pcfile;
この変更は、Func
構造体からx1
, x2
, x3
という3つのint32
型のフィールドを直接削除しています。これにより、Func
構造体のメモリフットプリントが削減され、Goバイナリ全体のサイズが小さくなります。コメントの変更は、args
とframe
フィールドも将来的に削除される可能性があることを示唆しています。
src/cmd/ld/lib.c
--- a/src/cmd/ld/lib.c
+++ b/src/cmd/ld/lib.c
@@ -2400,7 +2400,7 @@ pclntab(void)
// fixed size of struct, checked below
off = funcstart;
- end = funcstart + PtrSize + 6*4 + 5*4 + npcdata*4 + nfuncdata*PtrSize;
+ end = funcstart + PtrSize + 3*4 + 5*4 + npcdata*4 + nfuncdata*PtrSize;
if(nfuncdata > 0 && (end&(PtrSize-1)))
end += 4;
symgrow(ftab, end);
@@ -2417,9 +2417,6 @@ pclntab(void)
off = setuint32(ftab, off, ArgsSizeUnknown);
else
off = setuint32(ftab, off, cursym->args);
-
- // Dead space. TODO: Delete (and update all parsers).
- off = setuint32(ftab, off, 0);
// frame int32
// TODO: Remove entirely. The pcsp table is more precise.
@@ -2432,10 +2429,6 @@ pclntab(void)
else
off = setuint32(ftab, off, (uint32)cursym->text->to.offset+PtrSize);
- // Dead space. TODO: Delete (and update all parsers).
- off = setuint32(ftab, off, 0);
- off = setuint32(ftab, off, 0);
-
// pcsp table (offset int32)
off = addpctab(ftab, off, cursym, "pctospadj", pctospadj, 0);
pclntab
のサイズ計算において、Func
構造体の固定部分のサイズが6*4
から3*4
に削減されています。これは、削除された3つのint32
フィールド(12バイト)に対応します。また、以前はFunc
構造体内の「Dead space」として0を書き込んでいた部分が削除されました。これにより、リンカはよりコンパクトなpclntab
を生成します。
src/libmach/sym.c
--- a/src/libmach/sym.c
+++ b/src/libmach/sym.c
@@ -1563,6 +1563,15 @@ dumphist(char *name)
// Go 1.2 pcln table
// See golang.org/s/go12symtab.
+// Func layout
+#define FuncEntry (0)
+#define FuncName (pcptrsize)
+#define FuncArgs (pcptrsize+4)
+#define FuncFrame (pcptrsize+2*4)
+#define FuncPCSP (pcptrsize+3*4)
+#define FuncPCFile (pcptrsize+4*4)
+#define FuncPCLine (pcptrsize+5*4)
+
static int32 pcquantum;\
static int32 pcptrsize;\
static uvlong (*pcswav)(uvlong);\
@@ -1788,8 +1797,8 @@ go12pc2sp(uvlong pc)
f = go12findfunc(pc);
if(f == nil)
return ~(uvlong)0;
- entry = pcuintptr(f);
- off = pcswal(*(uint32*)(f+pcptrsize+6*4));
+ entry = pcuintptr(f+FuncEntry);
+ off = pcswal(*(uint32*)(f+FuncPCSP));
sp = pcvalue(off, entry, pc);
if(sp < 0)
return ~(uvlong)0;
@@ -1807,9 +1816,9 @@ go12fileline(char *str, int n, uvlong pc)
f = go12findfunc(pc);
if(f == nil)
return 0;
- entry = pcuintptr(f);
- fileoff = pcswal(*(uint32*)(f+pcptrsize+7*4));
- lineoff = pcswal(*(uint32*)(f+pcptrsize+8*4));
+ entry = pcuintptr(f+FuncEntry);
+ fileoff = pcswal(*(uint32*)(f+FuncPCFile));
+ lineoff = pcswal(*(uint32*)(f+FuncPCLine));
lno = pcvalue(lineoff, entry, pc);
fno = pcvalue(fileoff, entry, pc);
if(lno < 0 || fno <= 0 || fno >= nfiletab) {
@@ -1845,9 +1854,9 @@ havefile:
// quick.
for(i=0; i<nfunctab; i++) {
func = pcline + pcuintptr(functab+i*2*pcptrsize+pcptrsize);
- entry = pcuintptr(func);
- fp = pcline + pcswal(*(uint32*)(func+pcptrsize+7*4));
- lp = pcline + pcswal(*(uint32*)(func+pcptrsize+8*4));
+ entry = pcuintptr(func+FuncEntry);
+ fp = pcline + pcswal(*(uint32*)(func+FuncPCFile));
+ lp = pcline + pcswal(*(uint32*)(func+FuncPCLine));
fval = lval = -1;
fpc = lpc = entry;
fstartpc = fpc;
このファイルでは、Func
構造体内の各フィールドへのオフセットを定義する#define
マクロが導入されました。これにより、コードがより明確になり、Func
構造体のレイアウト変更に対する堅牢性が向上しました。例えば、pcsp
へのアクセスは以前のf+pcptrsize+6*4
からf+FuncPCSP
に変更されています。これは、Func
構造体から3つのint32
フィールドが削除されたため、オフセットが3*4=12
バイト分ずれたことを反映しています。
src/pkg/debug/gosym/pclntab.go
--- a/src/pkg/debug/gosym/pclntab.go
+++ b/src/pkg/debug/gosym/pclntab.go
@@ -339,7 +339,7 @@ func (t *LineTable) go12PCToLine(pc uint64) (line int) {
return -1
}
entry := t.uintptr(f)
- linetab := t.binary.Uint32(f[t.ptrsize+8*4:])
+ linetab := t.binary.Uint32(f[t.ptrsize+5*4:])
return int(t.pcvalue(linetab, entry, pc))
}
@@ -356,7 +356,7 @@ func (t *LineTable) go12PCToFile(pc uint64) (file string) {
return ""
}
entry := t.uintptr(f)
- filetab := t.binary.Uint32(f[t.ptrsize+7*4:])
+ filetab := t.binary.Uint32(f[t.ptrsize+4*4:])
fno := t.pcvalue(filetab, entry, pc)
if fno <= 0 {
return ""
@@ -384,8 +384,8 @@ func (t *LineTable) go12LineToPC(file string, line int) (pc uint64) {
for i := uint32(0); i < t.nfunctab; i++ {
f := t.Data[t.uintptr(t.functab[2*t.ptrsize*i+t.ptrsize:]):]
entry := t.uintptr(f)
- filetab := t.binary.Uint32(f[t.ptrsize+7*4:])
- linetab := t.binary.Uint32(f[t.ptrsize+8*4:])
+ filetab := t.binary.Uint32(f[t.ptrsize+4*4:])
+ linetab := t.binary.Uint32(f[t.ptrsize+5*4:])
pc := t.findFileLine(entry, filetab, linetab, int32(filenum), int32(line))
if pc != 0 {
return pc
このGoコードは、pclntab
から行番号やファイル名などの情報を抽出する際に、Func
構造体内のlinetab
やfiletab
へのオフセットを調整しています。t.ptrsize
はポインタのサイズ(4バイトまたは8バイト)を示し、それに続く*4
はint32
フィールドの数とサイズ(4バイト)を掛け合わせたものです。x1
, x2
, x3
の3つのint32
フィールドが削除されたため、オフセットが3*4=12
バイト分減少し、8*4
が5*4
に、7*4
が4*4
に変更されています。これにより、debug/gosym
パッケージは新しいpclntab
形式を正しく解析できるようになります。
関連リンク
- Go 1.2 Release Notes: https://go.dev/doc/go1.2 (Go 1.2の変更点全般について言及されていますが、シンボルテーブルの詳細は別途確認が必要です。)
- Go 1.2 Symbol Table Format (golang.org/s/go12symtab): このコミットのコード内で参照されているドキュメント。Go 1.2のシンボルテーブルの新しい形式について詳細に説明されています。
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコード
- Go言語のIssueトラッカー (Gerrit CL 11604043)
- Go 1.2 Symbol Table Format (golang.org/s/go12symtab) - このコミットのコード内で参照されているため、最も直接的な情報源です。
- Goのデバッグ情報に関する一般的な情報源 (例: Goのデバッガに関するブログ記事やドキュメント)
- Goのリンカに関する情報源 (例: Goのリンカの動作に関する技術記事)
- Goの
Func
構造体に関する情報源 (例: Goのランタイム内部構造に関する記事) - https://go.dev/doc/go1.2
- https://github.com/golang/go/commit/c75884185332458242c03b17014bc3801977f684
- https://golang.org/cl/11604043
- https://go.dev/src/cmd/ld/lib.c (コミット当時のコードとは異なる可能性があります)
- https://go.dev/src/libmach/sym.c (コミット当時のコードとは異なる可能性があります)
- https://go.dev/src/pkg/debug/gosym/pclntab.go (コミット当時のコードとは異なる可能性があります)
- https://go.dev/src/pkg/runtime/runtime.h (コミット当時のコードとは異なる可能性があります)