[インデックス 17466] ファイルの概要
このコミットは、Go言語のリンカ(cmd/ld
)におけるpclntab
(Program Counter Line Table)のファイルテーブルに関連する最適化とバグ修正を目的としています。具体的には、pclntab
内で不要なガベージ領域が割り当てられるのを防ぎ、リンカの堅牢性を向上させています。これにより、生成されるバイナリのサイズが最適化され、潜在的なデータ破損の問題が回避されます。
コミット
commit d011f0aa8945d2c72dc89e8229baf929efeaf8e3
Author: Ian Lance Taylor <iant@golang.org>
Date: Wed Sep 4 13:26:49 2013 -0700
cmd/ld: don't allocate unused garbage space in pclntab file table
Fixes #6319.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/13539043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d011f0aa8945d2c72dc89e8229baf929efeaf8e3
元コミット内容
cmd/ld: don't allocate unused garbage space in pclntab file table
Fixes #6319.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/13539043
変更の背景
このコミットは、Goリンカがpclntab
(Program Counter Line Table)を構築する際に発生していた非効率性と潜在的なバグを修正するために行われました。pclntab
はGoの実行可能ファイルに埋め込まれる重要なメタデータ構造であり、プログラムカウンタ(PC)の値をソースコードのファイル名と行番号にマッピングするために使用されます。これは、スタックトレースの生成、デバッグ、プロファイリングといったGoランタイムの多くの機能にとって不可欠です。
問題は、リンカがpclntab
内のファイル名情報を格納する際に、必要以上にメモリを割り当ててしまい、その結果として未使用の「ガベージ領域」が生成されていたことにあります。この未使用領域は、バイナリサイズの肥大化を招くだけでなく、symgrow
関数がシンボルデータの最大サイズ(maxp
)を超えて書き込みを行おうとした場合に、corrupt symbol data
という診断エラーを引き起こす可能性がありました。これは、リンカが内部的に管理するシンボルデータ構造の整合性が損なわれることを意味し、リンカのクラッシュや不正なバイナリの生成につながる恐れがありました。
このコミットは、ftabaddstring
関数におけるftab->np += n+1;
という行の削除によって、この過剰なメモリ割り当てを停止させ、pclntab
のファイルテーブルがより効率的に、かつ正確に構築されるようにします。これにより、バイナリサイズの最適化とリンカの安定性向上が図られました。
前提知識の解説
Goリンカ (cmd/ld
)
Goリンカ(cmd/ld
)は、Goプログラムのビルドプロセスにおいて、コンパイルされたオブジェクトファイル(.o
ファイル)と必要なライブラリを結合し、実行可能なバイナリを生成する役割を担っています。リンカは、シンボル解決(関数や変数の参照を実際のメモリアドレスに解決する)、コードの再配置、デバッグ情報の埋め込みなど、多くの複雑なタスクを実行します。
pclntab
(Program Counter Line Table)
pclntab
は、Goの実行可能ファイルに埋め込まれる重要なメタデータ構造です。その主な目的は、実行中のプログラムカウンタ(PC、つまり実行中の命令のアドレス)を、対応するソースコードのファイル名と行番号にマッピングすることです。このマッピングは、以下のGoランタイム機能に不可欠です。
- スタックトレース: パニック発生時やエラー発生時に、人間が読める形式のスタックトレースを生成し、各関数呼び出しがどのファイルと行番号で発生したかを示します。
- デバッグ: デバッガがソースレベルの情報を提供し、開発者がコードをステップ実行したり、変数を検査したりできるようにします。
- プロファイリング:
pprof
のようなツールが、CPU時間、メモリ割り当てなどのメトリクスを特定のソースコード行に関連付け、パフォーマンス分析を支援します。
pclntab
は、Goバイナリの特定のセクション(Linuxでは.gopclntab
、macOSでは__gopclntab
など)に配置され、Goバイナリの必須コンポーネントです。
シンボルテーブルと文字列テーブル
リンカは、プログラム内のすべてのシンボル(関数、変数、型など)に関する情報をシンボルテーブルに格納します。シンボルテーブルは、これらのシンボルの名前、アドレス、サイズ、型などの属性を管理します。
文字列テーブルは、シンボル名やファイル名など、リンカが扱う様々な文字列データを格納するために使用される領域です。リンカは、これらの文字列を直接シンボルテーブルに埋め込むのではなく、文字列テーブルに格納し、シンボルテーブルからはその文字列へのオフセット(位置)を参照することが一般的です。これにより、重複する文字列の格納を避け、バイナリサイズを削減できます。
symgrow
関数とftabaddstring
関数
コミットの変更箇所に登場するsymgrow
とftabaddstring
は、Goリンカの内部関数です。
symgrow(Sym *s, int32 siz)
: この関数は、特定のシンボルs
に関連付けられたデータ領域を、指定されたサイズsiz
まで拡張するために使用されます。リンカがシンボルにデータを追加する際に、現在の割り当てサイズが不足している場合に呼び出されます。ftabaddstring(Sym *ftab, char *s)
: この関数は、ファイルテーブル(ftab
)に文字列s
を追加するために使用されます。pclntab
のファイル名情報を格納する際に利用されます。
これらの関数は、リンカがバイナリを構築する過程で、動的にメモリを管理し、必要なデータを配置するために重要な役割を果たします。
技術的詳細
このコミットの技術的詳細は、Goリンカがpclntab
のファイルテーブルをどのように構築し、その過程でどのような問題が発生していたか、そしてその問題がどのように解決されたかに焦点を当てます。
pclntab
におけるファイル情報の格納
pclntab
は、ソースファイル名を行番号情報と関連付けて格納します。これは、実行時のPC値から正確なソースコードの位置を特定するために必要です。リンカは、コンパイルされた各Goパッケージからファイル名情報を収集し、これらをpclntab
内の特定の領域に配置します。
「未使用のガベージ領域」の発生
問題は、ftabaddstring
関数がファイルテーブルに文字列を追加する際の内部的な動作にありました。元のコードでは、文字列をコピーした後、ftab->np += n+1;
という行で、ファイルテーブルの現在のポインタ(np
)を文字列の長さ分だけ進めていました。しかし、symgrow
関数が既にftab->np
を適切なサイズに拡張しているにもかかわらず、この行が重複してポインタを進めていたため、文字列の終端を超えて余分な領域が「割り当てられた」かのように見えていました。この余分な領域は実際には使用されず、ガベージデータが含まれる可能性があり、これが「未使用のガベージ領域」として認識されていました。
symgrow
の堅牢性チェック
symgrow
関数は、シンボルs
のデータ領域を拡張する際に、s->np
(現在のデータサイズ)がs->maxp
(割り当てられた最大サイズ)を超えていないかをチェックする新しい堅牢性チェックが追加されました。
if(s->np > s->maxp) {
cursym = s;
diag("corrupt symbol data: np=%lld > maxp=%lld", (vlong)s->np, (vlong)s->maxp);
erorexit();
}
このチェックは、ftabaddstring
のような関数がsymgrow
の意図しない方法でnp
を更新し、割り当てられたバッファの境界を越えて書き込もうとする潜在的なバグを検出するために導入されました。もしnp
がmaxp
を超えていれば、それはシンボルデータが破損していることを意味し、リンカはエラーで終了します。このチェックは、今回のコミットで修正されるftabaddstring
のバグが、将来的に他の場所で同様の問題を引き起こすことを防ぐための防御的なプログラミングの一環です。
ftabaddstring
の修正
ftabaddstring
関数からftab->np += n+1;
の行が削除されたことで、symgrow
が既に適切にnp
を更新しているため、重複してポインタを進めることがなくなりました。これにより、ファイルテーブルに文字列が正確なサイズで追加され、未使用のガベージ領域が生成されることがなくなりました。
この修正は、Goリンカがpclntab
をより効率的かつ正確に構築することを保証し、結果として生成されるGoバイナリのサイズを最適化し、リンカの全体的な安定性と信頼性を向上させます。
コアとなるコードの変更箇所
このコミットでは、src/cmd/ld/data.c
とsrc/cmd/ld/lib.c
の2つのファイルが変更されています。
src/cmd/ld/data.c
--- a/src/cmd/ld/data.c
+++ b/src/cmd/ld/data.c
@@ -376,6 +376,12 @@ symgrow(Sym *s, int32 siz)
return;
+ if(s->np > s->maxp) {
+ cursym = s;
+ diag("corrupt symbol data: np=%lld > maxp=%lld", (vlong)s->np, (vlong)s->maxp);
+ erorexit();
+ }
+
if(s->maxp < siz) {
if(s->maxp == 0)
s->maxp = 8;
src/cmd/ld/lib.c
--- a/src/cmd/ld/lib.c
+++ b/src/cmd/ld/lib.c
@@ -2348,7 +2348,6 @@ ftabaddstring(Sym *ftab, char *s)
start = ftab->np;
symgrow(ftab, start+n+1);
strcpy((char*)ftab->p + start, s);
- ftab->np += n+1;
return start;
}
コアとなるコードの解説
src/cmd/ld/data.c
の変更
symgrow
関数に新しいif
ブロックが追加されました。
if(s->np > s->maxp) {
cursym = s;
diag("corrupt symbol data: np=%lld > maxp=%lld", (vlong)s->np, (vlong)s->maxp);
erorexit();
}
このコードは、symgrow
がシンボルs
のデータ領域を拡張しようとする前に、現在のデータサイズs->np
が割り当てられた最大サイズs->maxp
を超えていないかをチェックします。もしs->np
がs->maxp
よりも大きい場合、それはシンボルデータが破損していることを意味します。この状況は、リンカの内部的な不整合や、バッファオーバーフローのような問題を示唆しています。このような場合、リンカはdiag
関数を使って診断メッセージを出力し、erorexit()
を呼び出してプログラムを異常終了させます。
この追加は、リンカの堅牢性を高めるための防御的な措置です。これにより、潜在的なデータ破損が早期に検出され、不正なバイナリが生成されるのを防ぐことができます。
src/cmd/ld/lib.c
の変更
ftabaddstring
関数から以下の行が削除されました。
ftab->np += n+1;
この行は、ファイルテーブルftab
の現在のポインタnp
を、追加された文字列の長さn
と終端のnull文字(+1
)分だけ進める役割を担っていました。しかし、ftabaddstring
の直前で呼び出されているsymgrow(ftab, start+n+1);
関数が、既にftab->np
を適切に更新しているため、この行は冗長であり、かつ問題の原因となっていました。
symgrow
関数は、シンボル(この場合はファイルテーブルを表すftab
)のデータ領域を必要なサイズ(start+n+1
)まで拡張し、その過程でftab->np
を新しい終端位置に設定します。したがって、ftab->np += n+1;
という行は、np
を二重にインクリメントしてしまい、結果としてftab
のデータ領域の終端を超えてnp
が指すことになり、未使用のガベージ領域が生成される原因となっていました。
この冗長な行を削除することで、ftab->np
はsymgrow
によって正確に管理されるようになり、pclntab
のファイルテーブルに文字列が適切に配置され、不要なガベージ領域の割り当てがなくなりました。これにより、バイナリサイズの最適化とリンカの正確性が向上します。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/d011f0aa8945d2c72dc89e8229baf929efeaf8e3
- Go CL (Change List): https://golang.org/cl/13539043
参考にした情報源リンク
- Go
pclntab
に関する情報:- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGUn6fBOfCo99RgPX9Sfysx1AzaMb7MLtgCvSO2gStudtaxq2X5Iqyd_K6DVGjUaNpZ3u7r4gtpxYtXOsLw6G48XgC4-aHu0ZYCF6Dit9oft-qmKr_sxBH_Lrr7MfUbHBrqo3__YgdH70XZKUCofqMLsTQ8I47zRTWUyuKTFYPIh7ijaFrtoneZnkXCp9pki0MjUzUwCg==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGutrLSWzF6L-tlQhL3Sz6S3DfN6G2L-NbNo6mHZnKhkZfCigpy5Wpx5MRhhZcm2LaJrD9p4GV-a2o55jKe2fRd8VLg5KaHAWjt4uQqEP-fiGWGOvxcjg2CW-d85Rb64wqtwuknf8HVtCX73eiAJG6T8GJoszcH1_P47A==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFFpysRRGR6XOb0UKz64nLDXw_kGv6-oUNp-UiGldfHFCEBnZAU7g-Moz7jGj-obO-SgopcObDWxOzw8vlIDJKVRyUUAjFYyxr0v7zJ-2uSG2cclQUO6FcoXJTtqzBALrtBXPWW
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGOB3uCiTsXCUX4iGnl6fcaYeHZkMeJtmfky0E1L74U6XKlRJs6h_5KxZDzEVbuKywc0dvzW2i7NVHyTCQaFUbhmUpQHgLp3yYgeZutw98hNfvssDWEtIWjnifCRB_I7i3AMAuTi7CovQ==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEmRbgtAWEaR2_SdDNv2wEZKeC9ASHiYEDFDps4mv6B7ppVjtAS78fcUHWzZCd0kr4OME2f3meJ9TT187hxd5ebRNcduDszfhNcwWciCIMiQFN13koyDC2DrlpJFcbkn8ujUteXwZoPoc2pxF1osp6NAkxx3EEw9Nw__WAYLA==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGJaT4ALfu7ngavUHFEO5bE7zXCXaycGELvFfrq7uD2rNGttyLui0fLVHBCMLSKMoG1f1XnAYPlZPbqYrP5peeeRRkfpWGK499DT0z82akHIL81bAC7EUlIvGTHe4BXd5NQggMBzP27vMIQdh9j4FP_
- Goリンカに関する情報:
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQE6etI2aAWzZCMVA267SLhnVxD6zeT8XFR6DAx0mpZg6A5jrgnTCvM9l_hNHbX4w8gXFgLKvVY3i2jx8MTXHwbq-s2fSZMPIyVOMXBX3pWBb6gjdfKd-53IkW9AjMENWxhpx91XZdOqcogiG2b1qlFMMSD4-X5R4N0vMy9xUoWSxDOEhez8noo=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEJGJxCvvKUX6dswrwCeQbE4qty7_xIn1GFG4A7mXtg5PH61pA7aembuKwo3eTXMb4yD3sfSi12-sA8AF3tP0aAEDzZj-WKlk86VSf8py-LKG5LUK6RjsiLt74HjyKfUGdnBOqdQuMYXjFZ19iCdEwZCodw59uEHBoK0T2g332-PNjNDe5ijrrfjM2Yz7XeDZHPWkiYDFR8Ag==