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

[インデックス 17657] ファイルの概要

コミット

このコミットは、Goリンカ cmd/ld におけるバグ修正です。具体的には、ld -s オプション(シンボルテーブルを削除するオプション)を使用した場合に、pclntab (PC-line table) が誤って破棄されてしまう問題を修正しています。この修正により、ld -s を使用しても、スタックトレースやプロファイリングに必要な情報が保持されるようになります。

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/0d85d5423b314f7e43c787491501d2ae3d0ebe99

元コミット内容

commit 0d85d5423b314f7e43c787491501d2ae3d0ebe99
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Fri Sep 20 10:04:52 2013 -0400

    cmd/ld: fix "ld -s" to not discard pclntab.
    Fixes #6245.
    
    R=golang-dev, bradfitz, rsc
    CC=golang-dev
    https://golang.org/cl/13751045
---
 src/cmd/ld/lib.c | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c
index b3c3713e2d..f9c2773280 100644
--- a/src/cmd/ld/lib.c
+++ b/cmd/ld/lib.c
@@ -2378,9 +2378,6 @@ pclntab(void)
  	ftab->type = SPCLNTAB;
  	ftab->reachable = 1;
  
-\tif(debug['s'])\n-\t\treturn;\n-\n  	// See golang.org/s/go12symtab for the format. Briefly:\n  	//\t8-byte header\n  	//\tnfunc [PtrSize bytes]\n```

## 変更の背景

このコミットは、Goリンカ `cmd/ld` の `pclntab` (PC-line table) の扱いに関するバグを修正するために行われました。Goのバイナリには、実行時のスタックトレース生成やプロファイリングに不可欠な `pclntab` と呼ばれるセクションが含まれています。これは、プログラムカウンタ (PC) の値から対応するソースコードのファイル名や行番号を特定するための情報を提供します。

しかし、以前のリンカでは、`ld -s` オプション(シンボルテーブルを削除するオプション)が指定された場合に、この `pclntab` も誤って破棄されていました。シンボルテーブルはデバッグ情報の一部であり、通常はリリースビルドでサイズを削減するために削除されますが、`pclntab` はデバッグ情報とは異なり、実行時の重要な機能(パニック時のスタックトレース表示など)に必要です。

`pclntab` が削除されると、Goプログラムがパニックを起こした際に、どのファイルや行でエラーが発生したかを示す詳細なスタックトレースが表示されなくなります。これは、問題のデバッグを非常に困難にするため、重大な問題でした。この問題は [Issue 6245](https://github.com/golang/go/issues/6245) として報告され、このコミットによって修正されました。

## 前提知識の解説

このコミットを理解するためには、以下の概念について理解しておく必要があります。

*   **Goリンカ (`cmd/ld`)**: Go言語のコンパイラツールチェーンの一部であり、Goのソースコードから生成されたオブジェクトファイルを結合して実行可能なバイナリを生成する役割を担います。C/C++における `ld` コマンドと同様の機能を提供しますが、Go特有のランタイム情報やセクションの扱いが異なります。
*   **シンボルテーブル**: プログラム内の関数名、変数名、ファイル名などのシンボルと、それらがメモリ上のどこに配置されているかを示すアドレスとの対応関係を記録したデータ構造です。デバッグ時にソースコードレベルでの情報を提供するために使用されます。`ld -s` オプションは、このシンボルテーブルをバイナリから削除し、バイナリサイズを削減します。
*   **PC-line table (`pclntab`)**: プログラムカウンタ (PC) の値と、対応するソースコードのファイル名および行番号とのマッピングを記録したテーブルです。Goのランタイムは、パニック発生時やプロファイリング時に、この `pclntab` を参照してスタックトレースを生成します。シンボルテーブルとは異なり、`pclntab` は実行時の動作に不可欠な情報です。
*   **Goバイナリの構造**: Goの実行可能バイナリは、一般的なELF (Executable and Linkable Format) やMach-O (macOS) などのフォーマットに従いますが、Goランタイムが使用する独自のセクション(例: `.gopclntab`)を含んでいます。これらのセクションには、ガベージコレクション情報、型情報、そして `pclntab` などが含まれます。
*   **`debug['s']`**: Goリンカの内部で使われるデバッグフラグの一つで、`ld -s` オプションが指定された場合に `true` になります。

## 技術的詳細

このコミットの技術的な核心は、`cmd/ld` の `lib.c` ファイル内の `pclntab` 関数における変更です。

`pclntab` 関数は、Goバイナリに `pclntab` セクションを構築する役割を担っています。変更前のコードでは、この関数の冒頭に以下の条件分岐がありました。

```c
if(debug['s'])
    return;

このコードは、「もし debug['s'] フラグが true であれば(つまり ld -s オプションが指定されていれば)、pclntab 関数を即座に終了し、pclntab を生成しない」という意味になります。これは、pclntab がシンボルテーブルと同様にデバッグ情報の一部であるという誤解に基づいていたか、あるいは初期の設計段階での考慮不足によるものと考えられます。

しかし、前述の通り、pclntab はデバッグ情報とは異なり、Goランタイムがスタックトレースを生成するために不可欠な情報です。ld -s を使用してバイナリサイズを削減したい場合でも、pclntab は保持されるべきです。

このコミットでは、上記の3行のコードが削除されました。これにより、ld -s オプションが指定された場合でも pclntab 関数が常に実行され、pclntab セクションがGoバイナリに適切に埋め込まれるようになります。結果として、シンボルテーブルが削除されたバイナリでも、実行時に正確なスタックトレースが表示されるようになり、デバッグやプロファイリングの機能が損なわれることがなくなりました。

この修正は、Goの安定性とデバッグ可能性を向上させる上で重要な変更でした。

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

変更は src/cmd/ld/lib.c ファイルの pclntab 関数内で行われました。

--- a/src/cmd/ld/lib.c
+++ b/src/cmd/ld/lib.c
@@ -2378,9 +2378,6 @@ pclntab(void)
  	ftab->type = SPCLNTab;
  	ftab->reachable = 1;
  
-\tif(debug['s'])\n-\t\treturn;\n-\n  	// See golang.org/s/go12symtab for the format. Briefly:\n  	//\t8-byte header\n  	//\tnfunc [PtrSize bytes]\n```

具体的には、以下の3行が削除されました。

```c
if(debug['s'])
    return;

コアとなるコードの解説

削除されたコードは、pclntab 関数が debug['s'] フラグ(ld -s オプションが有効な場合に設定される)をチェックし、そのフラグが true であれば関数を早期に終了させるというものでした。

この pclntab 関数は、Goバイナリの .gopclntab セクションを構築する責任を負っています。このセクションには、プログラムカウンタ (PC) の値と、対応するソースコードのファイル名および行番号とのマッピング情報が含まれています。Goランタイムは、パニック発生時やプロファイリング時に、この情報を使用してスタックトレースを生成します。

変更前のコードでは、ld -s オプションが指定されると、この pclntab セクションが生成されませんでした。その結果、シンボル情報が削除されたバイナリでは、実行時にスタックトレースが正しく表示されず、デバッグが困難になるという問題が発生していました。

このコミットによって、上記の条件分岐が削除されたため、pclntab 関数は debug['s'] フラグの状態に関わらず常に実行されるようになりました。これにより、ld -s オプションを使用しても pclntab セクションが常にバイナリに含まれるようになり、Goプログラムの実行時デバッグ情報が適切に保持されるようになりました。

関連リンク

参考にした情報源リンク