Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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関数

コミットの変更箇所に登場するsymgrowftabaddstringは、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を更新し、割り当てられたバッファの境界を越えて書き込もうとする潜在的なバグを検出するために導入されました。もしnpmaxpを超えていれば、それはシンボルデータが破損していることを意味し、リンカはエラーで終了します。このチェックは、今回のコミットで修正されるftabaddstringのバグが、将来的に他の場所で同様の問題を引き起こすことを防ぐための防御的なプログラミングの一環です。

ftabaddstringの修正

ftabaddstring関数からftab->np += n+1;の行が削除されたことで、symgrowが既に適切にnpを更新しているため、重複してポインタを進めることがなくなりました。これにより、ファイルテーブルに文字列が正確なサイズで追加され、未使用のガベージ領域が生成されることがなくなりました。

この修正は、Goリンカがpclntabをより効率的かつ正確に構築することを保証し、結果として生成されるGoバイナリのサイズを最適化し、リンカの全体的な安定性と信頼性を向上させます。

コアとなるコードの変更箇所

このコミットでは、src/cmd/ld/data.csrc/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->nps->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->npsymgrowによって正確に管理されるようになり、pclntabのファイルテーブルに文字列が適切に配置され、不要なガベージ領域の割り当てがなくなりました。これにより、バイナリサイズの最適化とリンカの正確性が向上します。

関連リンク

参考にした情報源リンク