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

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

このコミットは、Go言語のリンカ (cmd/ld) における dwarf.c ファイルに対する変更です。dwarf.c は、Goプログラムの実行可能ファイルにデバッグ情報(DWARF形式)を生成する役割を担っています。このコミットは、以前の誤ったマージによって失われたDWARF関連の修正を元に戻し、デバッグ情報の正確性と堅牢性を向上させることを目的としています。

コミット

  • コミットハッシュ: f74b4d3de3e39fe066c9bc0109122156b273d5f8
  • 作者: Shenghou Ma minux.ma@gmail.com
  • コミット日時: 2013年3月22日 金曜日 03:54:14 +0800
  • 変更ファイル: src/cmd/ld/dwarf.c
  • 変更行数: 186行 (150行追加, 36行削除)

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

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

元コミット内容

cmd/ld: fix bad merge
CL 7504044 accidentally reverted part of CL 7891044 and 7552045, this CL
bring those part back.

R=golang-dev
TBR=rsc
CC=golang-dev
https://golang.org/cl/7950045

変更の背景

このコミットの背景には、Goプロジェクトのコードベース管理におけるマージの失敗があります。具体的には、CL 7504044 という変更リスト(Change List)が、以前に適用されていた CL 7891044CL 7552045 の一部の変更を意図せず元に戻してしまった(revertしてしまった)ことが原因です。これらの失われた変更は、Goリンカが生成するDWARFデバッグ情報に関連するものでした。

デバッグ情報は、プログラムの実行時にデバッガがソースコードレベルで変数の値を確認したり、ステップ実行したりするために不可欠です。DWARF情報の生成に問題があると、デバッグ体験が著しく損なわれる可能性があります。

このコミット f74b4d3de3e39fe066c9bc0109122156b273d5f8 の目的は、この「悪いマージ」によって失われたDWARF関連の重要な修正を src/cmd/ld/dwarf.c に再導入し、Goプログラムのデバッグ情報の正確性と完全性を回復することにあります。

前提知識の解説

Go リンカ (cmd/ld)

Go言語のビルドプロセスにおいて、cmd/ld はGoリンカとして機能します。これは、Goコンパイラによって生成されたオブジェクトファイル(.o ファイル)を結合し、必要なライブラリとリンクして、最終的な実行可能ファイルを生成するツールです。リンカは、プログラムの実行に必要なすべてのコードとデータを配置し、シンボル解決を行い、デバッグ情報やその他のメタデータを実行可能ファイルに埋め込む責任を負います。

DWARF (Debugging With Attributed Record Formats)

DWARFは、プログラムのデバッグ情報を格納するための標準的なフォーマットです。これは、コンパイラやリンカによって生成され、実行可能ファイルに埋め込まれます。デバッガは、このDWARF情報を使用して、ソースコードレベルでのデバッグを可能にします。DWARF情報には、以下のようなものが含まれます。

  • 変数情報: 変数の名前、型、メモリ上の位置。
  • 型情報: 構造体、配列、関数などの複雑なデータ型の定義。
  • 関数情報: 関数の名前、引数、戻り値、ローカル変数。
  • ソースコードと実行可能コードのマッピング: ソースファイルの行番号と、それに対応する機械語コードのアドレス。
  • スタックフレーム情報: 関数呼び出しスタックの構造。

DWARFは、デバッガがソースコードを理解し、ブレークポイントを設定したり、変数を検査したり、コールスタックをトレースしたりするために不可欠です。

CL (Change List)

Goプロジェクトでは、コードの変更は「Change List (CL)」という単位で管理されます。これは、Googleが開発したコードレビューシステムであるGerritで広く使用されている概念です。開発者は変更をCLとして提出し、レビューアがそのCLを承認すると、コードベースにマージされます。コミットメッセージに記載されている CL 7504044 などは、Gerritにおける特定の変更セットを指します。

技術的詳細

このコミットは、src/cmd/ld/dwarf.c ファイルに広範な変更を加えており、主にGoの型システムとDWARFデバッグ情報のマッピングを改善し、リロケーション(再配置)の取り扱いを強化しています。

  1. DWARF情報のリロケーションの強化:

    • static vlong inforeloco;static vlong inforelocsize;static Sym *dsym; が追加され、DWARF情報のリロケーション(再配置)に関するオフセットとサイズ、および関連するシンボルを追跡できるようになりました。これは、特に共有ライブラリや動的にロードされるモジュールにおいて、デバッグ情報が実行時に正しくアドレス解決されるために重要です。
    • putattr 関数内で、DW_FORM_block1 形式の属性が DW_CLS_ADDRESS クラスを持つ場合、リロケーション情報を追加するロジックが導入されました。これにより、DWARFのロケーション式(location expression)が、実行時のアドレスに依存する値を持つ場合に、リンカが適切に再配置情報を生成できるようになります。
    • DW_FORM_ref_addr 形式の属性(DWARF内の他のDIEへの参照)の処理が改善され、PtrSize (ポインタサイズ) に応じて VPUT (64ビット値の書き込み) または LPUT (32ビット値の書き込み) を使用するようになりました。これは、DWARFの参照アドレスのサイズがターゲットアーキテクチャのポインタサイズに依存する場合があるため、クロスプラットフォームでのデバッグ情報の互換性を高めます。
    • writeinforeloc 関数が追加され、DWARF情報のリロケーションセクションを書き込むようになりました。
    • dwarfaddmachoheaders 関数が変更され、Mach-O形式の実行可能ファイル(macOSなど)のヘッダに、新しく追加されたDWARF情報のリロケーションセクションのオフセットとサイズが設定されるようになりました。これにより、Mach-OバイナリにおけるDWARFデバッグ情報の正確な配置が保証されます。
  2. 型定義 (typedef) の適切な処理:

    • walktypedef 関数が新しく追加されました。この関数は、DWARFの DW_ABRV_TYPEDECL (typedef) を解決し、そのtypedefが参照する実際の型定義(DIE: Debugging Information Entry)を辿る役割を担います。これにより、デバッガがGoの型エイリアスや匿名型を適切に解釈し、元の型情報を正確に表示できるようになります。
    • find 関数(DWARF DIEを名前で検索する関数)が walktypedef を利用するように変更されました。これにより、型を検索する際にtypedefを自動的に解決し、より堅牢な型解決が可能になります。特に、循環参照を持つ型定義(例: type T *T のようなケース)の解決に役立ちます。
    • dotypedef 関数が追加されました。この関数は、特定のGoの型(配列、関数、インターフェース、ポインタ、スライス、構造体など)に対して、対応するDWARF型定義(DW_ABRV_ARRAYTYPE など)だけでなく、DW_ABRV_TYPEDECL を生成するように変更されました。これにより、Goの型システムがDWARFでより正確に表現され、デバッガがGoの型エイリアスや匿名型を適切に扱えるようになります。
    • defgotype 関数(Goの型をDWARF DIEに変換する関数)内で、Goの組み込み型に対して dotypedef が呼び出されるようになりました。これにより、Goの型がDWARFのtypedefとして適切に表現され、デバッガがGoの型エイリアスや匿名型を適切に扱えるようになります。
    • synthesizestringtypes, synthesizeslicetypes, synthesizechantypes といった型合成を行う関数でも walktypedef が利用されるようになり、型定義の解決が統一され、より堅牢になりました。
  3. ロケーション属性の改善:

    • newabslocexprattr 関数が変更され、DW_AT_location 属性(変数や関数のメモリ上の位置を示す属性)の生成方法が改善されました。以前は定数としてアドレスを埋め込んでいましたが、シンボルと関連付けてリロケーション可能なアドレスとして扱うように変更されました。これにより、デバッグ情報がより正確になり、特にアドレスが実行時に変動する可能性のあるシンボルに対して、デバッガが正しい位置を特定できるようになります。
  4. DWARFバージョンの修正:

    • writelines 関数と writeinfo 関数内で、DWARFバージョンが 3 から 2 に戻されています。これは、Goリンカが生成するDWARFのバージョンを明示的に指定している箇所で、以前の変更で誤ってバージョンが上がってしまったのを修正していると考えられます。DWARFバージョン2は、より広範なデバッガでサポートされている可能性があり、互換性の観点からこの修正が行われた可能性があります。
  5. PC (Program Counter) 範囲の正確性向上:

    • flushunit 関数および writelines 関数内で、DW_AT_high_pc (関数の終了アドレス) を設定する際に、単なるアドレス値だけでなく、関連するシンボル情報も渡すように変更されました。これにより、リロケーションが必要な場合にデバッグ情報が正しく調整され、関数のPC範囲がより正確に表現されるようになります。
    • DW_AT_low_pc および DW_AT_high_pc の属性が DW_CLS_ADDRESS クラスとして扱われるようになり、リロケーションの対象となることが明示されました。

これらの変更は、Goプログラムのデバッグ体験を向上させ、特に複雑な型システムや動的なリンキング環境において、デバッガがより正確で信頼性の高い情報を提供できるようにすることを目的としています。

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

このコミットにおける主要な変更箇所は以下の通りです。

  • src/cmd/ld/dwarf.c: ファイル全体にわたる変更ですが、特に以下の関数や構造体定義が重要です。

    • 新しい静的変数:
      static vlong inforeloco;
      static vlong inforelocsize;
      static Sym *dsym;
      
    • walktypedef 関数の追加:
      static DWDie*
      walktypedef(DWDie *die)
      {
          // ... (typedef解決ロジック)
      }
      
    • find 関数での walktypedef の利用:
      static DWDie*
      find(DWDie *die, char* name)
      {
          // ...
          die2 = walktypedef(die);
          if(die2 != die) {
              die = die2;
              goto top;
          }
          // ...
      }
      
    • putattr 関数の変更:
      • DW_FORM_block1DW_CLS_ADDRESS 処理の追加:
        case DW_FORM_block1:    // block
            if(cls == DW_CLS_ADDRESS) {
                // ... (リロケーション情報追加)
            }
        
      • DW_FORM_ref_addr での PtrSize に応じた VPUT/LPUT の利用:
        case DW_FORM_ref_addr:  // reference to a DIE in the .info section
            // ...
            if(PtrSize == 8)
                VPUT(((DWDie*)data)->offs);
            else
                LPUT(((DWDie*)data)->offs);
        
      • putattr の引数に abbrev を追加:
        putattr(int abbrev, int form, int cls, vlong value, char *data)
        
    • newabslocexprattr 関数の変更:
      static void
      newabslocexprattr(DWDie *die, vlong addr, Sym *sym)
      {
          newattr(die, DW_AT_location, DW_CLS_ADDRESS, addr, (char*)sym);
      }
      
    • dotypedef 関数の追加:
      static void
      dotypedef(DWDie *parent, char *name, DWDie *def)
      {
          // ... (typedef DIE生成ロジック)
      }
      
    • defgotype 関数での dotypedef の呼び出し:
      // KindArray, KindFunc, KindInterface, KindPtr, KindSlice, KindStruct の各ケースで
      dotypedef(&dwtypes, name, die);
      
    • synthesizestringtypes, synthesizeslicetypes, synthesizechantypes での walktypedef の利用:
      prototype = walktypedef(defgotype(lookup_or_diag("type.runtime._string")));
      // ...
      
    • flushunit 関数での pcsym 引数の追加と利用:
      flushunit(DWDie *dwinfo, vlong pc, Sym *pcsym, vlong unitstart, int32 header_length)
      {
          // ...
          newattr(dwinfo, DW_AT_high_pc, DW_CLS_ADDRESS, pc+1, (char*)pcsym);
          // ...
      }
      
    • writelines 関数での epcs シンボルの追跡と利用:
      // ...
      epcs = S; // 初期化
      // ...
      flushunit(dwinfo, epc, epcs, unitstart, headerend - unitstart - 10); // 呼び出し
      // ...
      epcs = s; // 更新
      // ...
      newattr(dwfunc, DW_AT_low_pc, DW_CLS_ADDRESS, s->value, (char*)s);
      newattr(dwfunc, DW_AT_high_pc, DW_CLS_ADDRESS, epc, (char*)s);
      // ...
      
    • DWARFバージョンの変更:
      // 複数箇所で WPUT(3) が WPUT(2) に変更
      WPUT(2); // dwarf version
      
    • writeinforeloc 関数の追加:
      static vlong
      writeinforeloc(void)
      {
          // ... (リロケーション情報の書き込み)
      }
      
    • dwarfaddmachoheaders 関数でのリロケーション情報の設定:
      msect->reloc = inforeloco;
      msect->nreloc = inforelocsize / 8;
      

コアとなるコードの解説

このコミットの核心は、Goの型システムとDWARFデバッグ情報の間のマッピングをより正確かつ堅牢にすること、そして生成されるデバッグ情報のリロケーション処理を改善することにあります。

型定義 (typedef) の適切な解決と生成

walktypedef 関数は、DWARFの DW_ABRV_TYPEDECL (typedef) を透過的に解決し、そのtypedefが指す実際の型定義(DIE)を返すことで、デバッガがGoの型エイリアスや匿名型を正しく解釈できるようにします。これは、Goの型システムが持つ柔軟性をDWARFで正確に表現するために不可欠です。find 関数や型合成を行う関数 (synthesizestringtypes など) で walktypedef を利用することで、型検索と解決のロジックが統一され、デバッグ情報の整合性が向上します。

さらに、dotypedef 関数と defgotype 関数でのその利用は、Goの組み込み型(配列、関数、構造体など)に対して、その型自体のDWARF定義に加えて、対応する DW_ABRV_TYPEDECL を生成することを意味します。これにより、Goの型がDWARFのtypedefとして適切に表現され、デバッガがGoの型エイリアスや匿名型を適切に扱えるようになります。例えば、Goの type MyInt int のような型エイリアスが、DWARFでも MyInt という名前のtypedefとして表現され、それが基本型 int を参照するようになります。

DWARF情報のリロケーション処理の強化

inforeloco, inforelocsize, dsym といった新しい静的変数の導入、および writeinforeloc 関数の追加は、DWARF情報自体が持つアドレス参照(例えば、変数のメモリ位置や関数の開始アドレス)が、リンカによって最終的な実行可能ファイルに配置される際に正しく「再配置」されるようにするためのものです。特に、putattr 関数内で DW_FORM_block1 がアドレスを表現する場合にリロケーション情報を追加するロジックは、DWARFのロケーション式が実行時のアドレスに依存する値を参照する際に、デバッガが正しいメモリ位置を特定できるようにするために重要です。

また、newabslocexprattr 関数が DW_AT_location 属性をシンボルと関連付けて生成するようになったことで、デバッグ情報内のアドレスが、リンカによって最終的に決定されるシンボルのアドレスに基づいて正しく調整されるようになります。これは、特に共有ライブラリや動的にロードされるコードにおいて、デバッグ情報が実行時に正しく機能するために不可欠です。

Mach-OヘッダにDWARFリロケーション情報を追加する変更は、macOSのようなシステムで生成される実行可能ファイルにおいて、デバッグ情報が正しくロードされ、デバッガが利用できるようになることを保証します。

DWARFバージョンの調整

DWARFバージョンを 3 から 2 に戻す変更は、Goリンカが生成するDWARFの互換性を確保するためのものです。DWARFバージョン2は、より多くのデバッガやツールで広くサポートされているため、この変更はGoプログラムのデバッグ体験の安定性と移植性を向上させる可能性があります。

これらの変更は全体として、Goリンカが生成するDWARFデバッグ情報の品質、正確性、およびデバッガとの互換性を大幅に向上させることを目的としています。これにより、Go開発者はよりスムーズで信頼性の高いデバッグ体験を得ることができます。

関連リンク

参考にした情報源リンク

  • DWARF Debugging Information Format Standard: https://dwarfstd.org/ (DWARFの公式仕様)
  • Go Compiler and Linker Documentation: (Goの公式ドキュメントやソースコード内のコメント、Goのリンカに関するブログ記事などを参照)
  • Gerrit Code Review: https://www.gerritcodereview.com/ (Gerritに関する情報)
  • Mach-O File Format: (macOSの実行可能ファイル形式に関するドキュメントや記事を参照)