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

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

このコミットは、Go言語のリンカ (cmd/ld) におけるGDBスクリプトのパスに関する修正です。具体的には、デバッグ情報(DWARF)の生成時にGDBスクリプトのパスを特定するロジックが、Goランタイムのファイル名変更に対応していなかった問題を解決しています。

コミット

commit f8e9bbe475f014aab614000bde498961149539c5
Author: Wei Guangjing <vcc.163@gmail.com>
Date:   Tue Jan 31 10:32:24 2012 -0500

    cmd/ld: fix gdbscript
    
    R=golang-dev, lvd
    CC=golang-dev
    https://golang.org/cl/5601058

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

https://github.com/golang/go/commit/f8e9bbe475f014aab614000bde498961149539c5

元コミット内容

cmd/ld: fix gdbscript

このコミットは、Go言語のリンカ (cmd/ld) がGDBデバッガで使用するスクリプトのパスを正しく特定できない問題を修正します。

変更の背景

Go言語のデバッグでは、GDB (GNU Debugger) を利用してGoプログラムの実行を追跡し、変数やスタックトレースなどを検査することが一般的です。Goのツールチェインは、GDBがGo特有のデータ構造(goroutine、slice、mapなど)を理解し、適切に表示できるようにするためのPythonスクリプト (runtime/runtime-gdb.py) を提供しています。

このスクリプトのパスは、Goプログラムのデバッグ情報(DWARF)内に埋め込まれることがあります。リンカ (cmd/ld) は、このGDBスクリプトのパスを特定するために、Goランタイムのソースファイルパスを検索していました。

変更の背景には、Goランタイムの内部的なファイル名変更があります。以前は runtime/runtime_defs.go というファイルが存在していましたが、Goのビルドプロセスやクロスコンパイルの仕組みの進化に伴い、このファイルが runtime/zruntime_defs のような形式にリネームされたか、あるいはその内容が別のファイルに統合された可能性があります。

リンカ内の finddebugruntimepath 関数は、GDBスクリプトのパスを特定するために runtime/runtime_defs.go という文字列をハードコードして検索していました。このファイル名が変更されたため、リンカはGDBスクリプトの正しいパスを見つけられなくなり、結果としてGDBでのデバッグ体験に問題が生じる可能性がありました。このコミットは、このファイル名の変更に対応し、GDBスクリプトのパス特定ロジックを更新することで、デバッグ機能の健全性を維持することを目的としています。

前提知識の解説

Go言語のリンカ (cmd/ld)

Go言語のビルドプロセスにおいて、リンカ (cmd/ld) は非常に重要な役割を担います。コンパイラによって生成されたオブジェクトファイル(.o ファイル)や、Go標準ライブラリ、サードパーティライブラリのアーカイブファイル(.a ファイル)を結合し、実行可能なバイナリファイルを生成します。この過程で、シンボルの解決、メモリレイアウトの決定、そしてデバッグ情報の埋め込みなどが行われます。

DWARF (Debugging With Arbitrary Record Formats)

DWARFは、プログラムのデバッグ情報を格納するための標準的なフォーマットです。コンパイラやリンカによって生成された実行可能ファイルに埋め込まれ、デバッガがソースコードレベルでのデバッグ(変数名の表示、行番号へのマッピング、スタックトレースの解釈など)を行うために利用されます。Go言語のバイナリもDWARF形式のデバッグ情報を含んでおり、GDBなどのデバッガがこれを読み取ってデバッグセッションを支援します。

GDB (GNU Debugger)

GDBは、GNUプロジェクトによって開発された強力なコマンドラインデバッガです。C、C++、Goなど、多くのプログラミング言語に対応しており、プログラムの実行を一時停止させたり、ステップ実行したり、変数の値を検査したり、メモリの内容を調べたりする機能を提供します。GoプログラムをGDBでデバッグする際には、Goランタイムの内部構造をGDBが理解できるように、特別なPythonスクリプト(runtime/runtime-gdb.py)がロードされることが一般的です。

runtime/runtime-gdb.py

これはGo言語のソースツリーに含まれるPythonスクリプトで、GDBがGoプログラムをより効果的にデバッグできるようにするための拡張機能を提供します。具体的には、Goのgoroutine、slice、map、interfaceなどの複雑なデータ型をGDBが認識し、人間が読みやすい形式で表示するためのGDBコマンドやフォーマッタが定義されています。このスクリプトが正しくロードされないと、GDBでのGoプログラムのデバッグが非常に困難になります。

strstr 関数

C言語の標準ライブラリ関数の一つで、ある文字列(haystack)の中に別の文字列(needle)が最初に現れる位置を検索します。見つかった場合は、needle が始まる位置へのポインタを返します。見つからない場合は NULL を返します。このコミットでは、ファイルパス文字列の中に特定のランタイムファイル名が含まれているかを検出するために使用されています。

技術的詳細

このコミットの技術的な核心は、Goリンカの src/cmd/ld/dwarf.c ファイル内の finddebugruntimepath 関数にあります。この関数は、Goプログラムのビルド履歴(histfile)を走査し、GDBスクリプトのパスを特定しようとします。

元のコードでは、GDBスクリプトのパスを特定するために、ビルド履歴内のファイルパスに "runtime/runtime_defs.go" という文字列が含まれているかを strstr 関数で検索していました。

// 変更前
if ((c = strstr(histfile[i], "runtime/runtime_defs.go")) != nil) {
    // ...
}

しかし、Goランタイムの内部的な変更により、このファイル名が "runtime/zruntime_defs" のような形式に変更されたか、あるいはその内容がこの新しいプレフィックスを持つファイルに移動しました。z プレフィックスは、Goのビルドシステムにおいて、特定のアーキテクチャやOSに依存するコード、あるいは自動生成されたコードを示すためによく使用されます。例えば、zversion.gozgoarch.go などがあります。

この変更に対応するため、コミットでは検索対象の文字列を "runtime/zruntime_defs" に変更しています。

// 変更後
if ((c = strstr(histfile[i], "runtime/zruntime_defs")) != nil) {
    // ...
}

これにより、リンカはGoランタイムの最新のファイル命名規則に適合し、GDBスクリプト (runtime/runtime-gdb.py) の正しいパスをデバッグ情報に埋め込むことができるようになります。この修正は、Goプログラムのデバッグ体験を維持し、GDBがGoの内部構造を正しく解釈できるようにするために不可欠です。

memmove 関数は、gdbscript バッファにパスのプレフィックスとGDBスクリプトのファイル名を結合してコピーするために使用されています。strlen は文字列の長さを取得します。

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

--- a/src/cmd/ld/dwarf.c
+++ b/src/cmd/ld/dwarf.c
@@ -1601,7 +1601,7 @@ finddebugruntimepath(void)
 	char *c;
 
 	for (i = 1; i < histfilesize; i++) {
-		if ((c = strstr(histfile[i], "runtime/runtime_defs.go")) != nil) {
+		if ((c = strstr(histfile[i], "runtime/zruntime_defs")) != nil) {
 			l = c - histfile[i];
 			memmove(gdbscript, histfile[i], l);
 			memmove(gdbscript + l, "runtime/runtime-gdb.py", strlen("runtime/runtime-gdb.py") + 1);

コアとなるコードの解説

変更は src/cmd/ld/dwarf.c ファイルの finddebugruntimepath 関数内、1601行目付近にあります。

  • 変更前: if ((c = strstr(histfile[i], "runtime/runtime_defs.go")) != nil)
    • この行は、ビルド履歴 (histfile[i]) の各エントリを調べ、その中に "runtime/runtime_defs.go" という文字列が含まれているかを検索していました。これは、Goランタイムの定義ファイルがこのパスにあることを前提としていました。
  • 変更後: if ((c = strstr(histfile[i], "runtime/zruntime_defs")) != nil)
    • この行は、検索対象の文字列を "runtime/zruntime_defs" に変更しています。これにより、Goランタイムのファイル名変更に対応し、リンカがGDBスクリプトのベースパスを正しく特定できるようになります。zruntime_defs は、Goのビルドシステムにおける特定のファイル命名規則(自動生成されたり、特定のビルドターゲットに特化したりするファイルによく見られる z プレフィックス)を反映しています。

この修正により、Goプログラムのデバッグ情報に埋め込まれるGDBスクリプトのパスが常に最新のランタイムファイル構造と一致するようになり、GDBでのデバッグがスムーズに行えるようになります。

関連リンク

参考にした情報源リンク

  • Go言語のソースコード (特に src/cmd/ld および src/runtime ディレクトリ)
  • Go言語のIssueトラッカーやコードレビューシステム (Gerrit)
  • GDBのドキュメンテーション
  • DWARFの仕様書
  • Go言語のデバッグに関するコミュニティの議論やブログ記事