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

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

このコミットは、Go言語の初期のランタイムにおけるデバッグ情報の取り扱い、特にプログラムカウンタ(PC)からソースコードの行番号へのマッピング(pc/line table)の正確性を向上させるための変更です。対象ファイルは src/libmach_amd64/sym.c で、これはAMD64アーキテクチャ向けの機械依存ライブラリの一部であり、シンボルテーブルの処理を担当しています。

コミット

commit 1ad1044b2db590236fd7c22cb0b0bab8328207f3
Author: Rob Pike <r@golang.org>
Date:   Fri Jun 13 18:15:30 2008 -0700

    hack to find first instruction for decoding the pc/line table properly.
    
    SVN=122792

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

https://github.com/golang/go/commit/1ad1044b2db590236fd7c22cb0b0bab8328207f3

元コミット内容

hack to find first instruction for decoding the pc/line table properly. SVN=122792

変更の背景

このコミットの背景には、Go言語のデバッグツールがプログラムの実行アドレス(プログラムカウンタ、PC)から対応するソースコードの行番号を正確に特定できない問題があったことが示唆されています。コミットメッセージにある「hack to find first instruction for decoding the pc/line table properly」という記述は、pc/line table のデコードが正しく行われるように、実行可能コードの最初の命令のアドレスを正確に特定する必要があったことを示しています。

Go言語の初期は、Plan 9のツールチェインをベースにしていました。Plan 9のリンカやアセンブラは、実行可能ファイルのシンボル情報やテキストセグメントの開始アドレスを管理しますが、何らかの理由で txtstart (テキストセグメントの開始アドレス) が pc2line 関数が期待する正確な開始点と一致しない場合があったと考えられます。これにより、デバッガがPC値を行番号に変換する際に、オフセットがずれてしまい、誤った行番号が表示されるなどの問題が発生していた可能性があります。

この「ハック」は、シンボルテーブルから明示的に最初の命令のアドレス (firstinstr) を見つけ出すことで、この不整合を修正し、pc/line table のデコードをより堅牢にすることを目的としています。

前提知識の解説

  • プログラムカウンタ (PC): CPUが次に実行する命令のアドレスを保持するレジスタ。デバッグ時には、このPC値から現在実行中のソースコードの行を特定するために使用されます。
  • PC/Line Table (PC-Line Table): 実行可能ファイル内に含まれるデバッグ情報の一部で、プログラムカウンタ(PC)の値と、それに対応するソースコードのファイル名および行番号のマッピングを定義したテーブルです。デバッガはこのテーブルを参照して、実行中のコードがソースコードのどの部分に該当するかを特定します。
  • シンボルテーブル (Symbol Table): コンパイルされたプログラム内のシンボル(関数名、変数名など)と、それらがメモリ上で配置されているアドレスとの対応関係を記録したデータ構造です。デバッガやプロファイラは、このテーブルを利用して、人間が読めるシンボル名と機械語のアドレスを相互に変換します。
  • src/libmach_amd64/sym.c: Go言語の初期のツールチェインにおける、AMD64アーキテクチャ向けの機械依存ライブラリの一部です。このファイルは、実行可能ファイルからシンボル情報を読み込み、解析し、デバッグツールが利用できる形式に変換する役割を担っていました。特に、buildtbls 関数はシンボルテーブルを構築し、pc2line 関数はPC値を行番号に変換する主要なロジックを含んでいます。
  • TEXT シンボル: シンボルテーブルにおいて、実行可能コード(関数など)の開始点を示すシンボルです。Goのコンパイラやリンカは、各関数のエントリポイントを TEXT シンボルとして出力します。
  • uvlong: unsigned long long の略で、符号なし64ビット整数型を指します。アドレスやサイズなど、大きな数値を扱う際に使用されます。
  • txtstart: 実行可能ファイルのテキストセグメント(コードが格納されている領域)の開始アドレスを示す変数です。
  • pc2line 関数: プログラムカウンタ(PC)の値を受け取り、対応するソースコードの行番号を返す関数です。この関数は、pc/line table を参照してマッピングを行います。

技術的詳細

このコミットは、pc2line 関数が正確な行番号を返すために必要な、実行可能コードの「真の」開始アドレスを特定するという問題に対処しています。

従来の pc2line 関数は、txtstart という変数(テキストセグメントの開始アドレス)を基準にしてPC値を行番号にマッピングしていました。しかし、何らかの理由で txtstart が必ずしも実行可能コードの最初の命令の正確なアドレスを指していない場合があったようです。これは、リンカの挙動、セグメントの配置、あるいはデバッグ情報の生成方法に起因する可能性が考えられます。

このコミットでは、firstinstr という新しい静的変数を導入し、シンボルテーブルを走査して TEXT シンボル(関数の開始点)の中で最も小さいアドレスを持つものを見つけ出すことで、実行可能コードの最初の命令の正確なアドレスを特定します。

具体的には、buildtbls 関数内でシンボルテーブルを構築する際に、各 TEXT シンボルの value (アドレス) をチェックし、firstinstr がまだ設定されていないか、現在の TEXT シンボルのアドレスが firstinstr よりも小さい場合に、firstinstr をそのアドレスで更新します。これにより、firstinstr にはプログラム全体の最初の実行可能命令のアドレスが格納されることになります。

そして、pc2line 関数内で、currpc (現在のPC値の基準点) を設定する際に、firstinstr が設定されていれば txtstart の代わりに firstinstr を使用するように変更されています。これにより、pc2line 関数は、テキストセグメントの論理的な開始点ではなく、実際にコードが始まる物理的なアドレスを基準にして行番号のデコードを開始できるようになり、デバッグ情報の正確性が向上します。

コミットメッセージの「hack」という言葉は、このアプローチが根本的なリンカやコンパイラの挙動の修正ではなく、デバッグ情報の正確性を確保するための回避策であることを示唆しています。

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

src/libmach_amd64/sym.c ファイルにおいて、以下の変更が行われました。

  1. 新しい静的変数の追加:
    static	uvlong	firstinstr;		/* as found from symtab; needed for amd64 */
    
  2. buildtbls 関数内での firstinstr の初期化:
    buildtbls(void)
    {
        // ...
        firstinstr = 0;
        // ...
    }
    
  3. buildtbls 関数内での firstinstr の更新ロジック: TEXT シンボルを処理する switch 文の case 'T': ブロック内に追加。
    		case 'T':
    		case 't':
    			// ...
    			if (firstinstr == 0 || p->value < firstinstr)
    				firstinstr = p->value;
    			// ...
    
  4. pc2line 関数内での currpc の設定変更:
    pc2line(uvlong pc)
    {
        // ...
        if (firstinstr != 0)
            currpc = firstinstr-mach->pcquant;
        else
            currpc = txtstart-mach->pcquant;
        // ...
    }
    
  5. デバッグ用 print 文の追加とコメントアウト:
    • fileline 関数内に print("line %d\\n", line); が追加。
    • buildtbls 関数内のループで //print("sym %d type %c name %s value %llux\\n", p-symbols, p->type, p->name, p->value); がコメントアウト。

コアとなるコードの解説

  • static uvlong firstinstr;: firstinstr は、シンボルテーブルから見つけ出された最初の実行可能命令のアドレスを保持するための静的変数です。static キーワードにより、この変数は sym.c ファイル内でのみアクセス可能です。uvlong 型は、アドレスを格納するのに十分な大きさの符号なし整数型です。コメント /* as found from symtab; needed for amd64 */ は、この変数がシンボルテーブルから取得され、特にAMD64アーキテクチャで必要とされることを示唆しています。

  • firstinstr = 0;: buildtbls 関数がシンボルテーブルの構築を開始する前に、firstinstr0 で初期化しています。これは、最初の TEXT シンボルが見つかるまで、有効なアドレスが設定されていないことを示すためです。

  • if (firstinstr == 0 || p->value < firstinstr) firstinstr = p->value;: このコードは、buildtbls 関数内でシンボルテーブルを走査し、各シンボルを処理するループの中にあります。p は現在のシンボルを指すポインタです。p->type'T' または 't' (テキストシンボル、つまり実行可能コードの開始点) である場合にこのロジックが実行されます。

    • firstinstr == 0: これは、まだ firstinstr が初期値のままで、最初の TEXT シンボルが見つかっていないことを意味します。この場合、現在のシンボルのアドレス p->valuefirstinstr に設定されます。
    • p->value < firstinstr: 既に firstinstr に値が設定されているが、現在の TEXT シンボルのアドレス p->value がそれよりも小さい場合、firstinstrp->value で更新します。これにより、シンボルテーブル全体で最もアドレスの小さい TEXT シンボル(すなわち、プログラムの最初の命令)のアドレスが firstinstr に確実に格納されます。
  • if (firstinstr != 0) currpc = firstinstr-mach->pcquant; else currpc = txtstart-mach->pcquant;: これは pc2line 関数内の変更です。pc2line はPC値を行番号に変換する際に、currpc を基準点として使用します。

    • firstinstr != 0: firstinstr0 でない場合(つまり、シンボルテーブルから最初の命令のアドレスが正常に特定された場合)、currpctxtstart の代わりに firstinstr を基準に設定されます。mach->pcquant は、PC値の量子化(アラインメント)に関連するオフセットであると考えられます。
    • else currpc = txtstart-mach->pcquant;: firstinstr0 のままの場合(何らかの理由で最初の命令が見つからなかった場合など)、フォールバックとして従来の txtstart が使用されます。 この変更により、pc2line 関数は、より正確な実行可能コードの開始点からPC-Lineテーブルのデコードを開始できるようになり、デバッグ情報の正確性が向上します。
  • print("line %d\\n", line); とコメントアウトされた print: これらはデバッグ中に一時的に追加された print 文であり、コミットの主要な機能変更とは直接関係ありません。fileline 関数内の print は、行番号が正しく取得されているかを確認するためのものであり、buildtbls 内のコメントアウトされた print は、シンボル情報の詳細を確認するためのものであったと考えられます。

関連リンク

  • Go言語の初期のデバッグツールに関する情報(例:gdb との連携、Plan 9 ツールチェインの利用)
  • Plan 9 の libmach ライブラリに関するドキュメント
  • Go言語の runtime およびデバッグ情報の進化に関する議論

これらの情報は、2008年のコミットであるため、公式ドキュメントやアーカイブされたメーリングリストなどを検索する必要があります。

参考にした情報源リンク

  • Go言語の公式リポジトリ (GitHub): https://github.com/golang/go
  • コミットハッシュ: 1ad1044b2db590236fd7c22cb0b0bab8328207f3
  • (必要に応じて、Go言語の初期の歴史やPlan 9ツールチェインに関するWeb検索結果)
    • google_web_search を使用して、「Go language early debugging pc line table」「Plan 9 toolchain libmach sym.c」などのキーワードで検索し、関連する情報源を特定しました。
    • 特に、Go言語の初期の設計思想や、Plan 9からの影響について言及している記事やドキュメントが参考になりました。
    • Go言語のソースコード自体も、当時の実装を理解する上で重要な情報源です。