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

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

コミット

commit ebe1eb6537d4873863db4a526250a0e5ddab41b2
Author: Graham King <graham4king@gmail.com>
Date:   Mon Mar 3 11:11:04 2014 -0800

    cmd/ld: DWARF opcode base to 10
    
    DWARF 2 has 9 standard opcodes, so dwarfdump expects us to use an
    opcode base of at least 10. Previously we used 5.
    
    Discussion:
    https://groups.google.com/forum/#!topic/golang-dev/d-BqpPgalzc
    
    LGTM=josharian, rsc
    R=golang-codereviews, gobot, rsc, josharian, iant, bradfitz
    CC=golang-codereviews
    https://golang.org/cl/69320043

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

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

元コミット内容

cmd/ld: DWARF opcode base to 10

このコミットは、Goリンカ(cmd/ld)におけるDWARFデバッグ情報の生成において、オペコードのベース値を5から10に変更するものです。

変更の背景

この変更の背景には、DWARF 2仕様とdwarfdumpユーティリティの期待値との不整合がありました。DWARF (Debugging With Attributed Record Formats) は、コンパイラが生成するデバッグ情報の標準フォーマットであり、デバッガが実行ファイル内のアドレスをソースコードのファイル名、行番号、変数情報などにマッピングするために使用されます。

DWARFのラインナンバープログラム(Line Number Program)は、ソースコードの行と実行ファイルの命令アドレスとのマッピングを記述するために使用されます。このプログラムは、特別なオペコードと標準オペコードで構成されます。DWARF 2仕様では、標準オペコードの数が9つと定義されており、dwarfdumpのようなDWARFデバッグ情報を解析するツールは、この標準オペコードの数に基づいてオペコードのベース値を期待します。

Goリンカは以前、このオペコードのベース値を5としていました。しかし、dwarfdumpはDWARF 2の9つの標準オペコードを正しく解釈するために、オペコードのベース値が少なくとも10であることを期待していました。この不整合により、Goリンカが生成したDWARF情報がdwarfdumpで正しく解析されない、あるいは警告が表示されるといった問題が発生していました。このコミットは、この問題を解決し、Goが生成するDWARF情報がより広範なツールと互換性を持つようにするために行われました。

前提知識の解説

  • DWARF (Debugging With Attributed Record Formats): DWARFは、プログラムのデバッグ情報を表現するための標準的なフォーマットです。コンパイラはソースコードをコンパイルする際に、変数、関数、型、ソースファイルの行番号と実行可能コードのアドレスのマッピングなどのデバッグ情報を生成し、これを実行ファイルに埋め込みます。デバッガはこのDWARF情報を読み取り、ユーザーがソースコードレベルでプログラムをデバッグできるようにします。

  • Goリンカ (cmd/ld): Go言語のビルドプロセスにおいて、cmd/ldはGoコンパイラによって生成されたオブジェクトファイルを結合し、最終的な実行可能バイナリを生成する役割を担うリンカです。このリンカは、デバッグ情報(DWARF)の生成と、それをバイナリに埋め込む処理も行います。

  • DWARF Line Number Program: DWARFのデバッグ情報の一部として、ラインナンバープログラムがあります。これは、ソースコードの特定の行が、コンパイルされたバイナリのどの命令アドレスに対応するかを記述するためのバイトコードのシーケンスです。デバッガはこれを利用して、実行中のプログラムの現在の命令がソースコードのどの行に相当するかを特定し、ブレークポイントの設定やステップ実行を可能にします。

  • オペコード (Opcode): オペコードは、ラインナンバープログラム内で特定の操作を指示するバイトコードです。DWARFのラインナンバープログラムには、標準オペコードと特別なオペコードがあります。標準オペコードは、行番号やアドレスのインクリメントなど、一般的な操作を表現します。特別なオペコードは、より効率的に行番号とアドレスの変更を表現するために使用されます。

  • オペコードベース (Opcode Base): DWARFのラインナンバープログラムにおいて、opcode_baseは特別なオペコードの開始値を定義します。標準オペコードは1からopcode_base - 1までの値を取ります。つまり、opcode_baseの値が大きいほど、より多くの標準オペコードが利用可能であることを意味します。

  • dwarfdump: dwarfdumpは、実行ファイルやオブジェクトファイルに含まれるDWARFデバッグ情報を解析し、人間が読める形式で表示するためのコマンドラインユーティリティです。デバッグ情報が正しく生成されているかを確認したり、デバッグ情報の構造を分析したりする際に使用されます。

技術的詳細

DWARFのラインナンバープログラムは、ステートマシンとして動作します。このステートマシンは、ソースファイルの行番号、命令アドレス、列番号などの状態を保持し、オペコードを処理することでこれらの状態を更新していきます。

ラインナンバープログラムのヘッダには、いくつかの重要な情報が含まれています。その一つがopcode_baseです。この値は、特別なオペコードが始まるオフセットを示します。DWARF 2仕様では、9つの標準オペコードが定義されています。これらの標準オペコードは、通常、1から9までの値にマッピングされます。したがって、dwarfdumpのようなツールがこれらの9つの標準オペコードを正しく認識するためには、opcode_baseが少なくとも10である必要があります(1から9までの標準オペコードの後に特別なオペコードが続くため)。

Goリンカは以前、OPCODE_BASEを5と定義していました。これは、標準オペコードが1から4までしか利用できないことを意味し、DWARF 2仕様で定義されている残りの標準オペコード(5から9)が正しく解釈されない可能性がありました。この不整合が、dwarfdumpがGoが生成したDWARF情報に対して警告を発したり、一部の情報を正しく表示できなかったりする原因となっていました。

このコミットでは、OPCODE_BASEを10に変更することで、DWARF 2仕様の9つの標準オペコードすべてが正しく認識されるようになります。これにより、Goリンカが生成するDWARF情報が、dwarfdumpを含む他のDWARFツールとの互換性が向上します。

また、writelines関数内でstandard_opcode_lengths配列の初期化も変更されています。OPCODE_BASEが10になったことで、配列のサイズが拡張され、標準オペコード5から9に対応するエントリが追加されています。これにより、各標準オペコードが取るオペランドの数が正しく設定され、ラインナンバープログラムの解釈がより正確になります。

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

変更は src/cmd/ld/dwarf.c ファイルに集中しています。

--- a/src/cmd/ld/dwarf.c
+++ b/src/cmd/ld/dwarf.c
@@ -1412,7 +1412,7 @@ finddebugruntimepath(LSym *s)
 enum {
 	LINE_BASE = -1,
 	LINE_RANGE = 4,
-	OPCODE_BASE = 5
+	OPCODE_BASE = 10
 };
 
 static void
@@ -1541,11 +1541,16 @@ writelines(void)
 	cput(1);   // default_is_stmt
 	cput(LINE_BASE);     // line_base
 	cput(LINE_RANGE);    // line_range
-	cput(OPCODE_BASE);   // opcode_base (we only use 1..4)
+	cput(OPCODE_BASE);   // opcode_base
 	cput(0);   // standard_opcode_lengths[1]
 	cput(1);   // standard_opcode_lengths[2]
 	cput(1);   // standard_opcode_lengths[3]
 	cput(1);   // standard_opcode_lengths[4]
+	cput(1);   // standard_opcode_lengths[5]
+	cput(0);   // standard_opcode_lengths[6]
+	cput(0);   // standard_opcode_lengths[7]
+	cput(0);   // standard_opcode_lengths[8]
+	cput(1);   // standard_opcode_lengths[9]
 	cput(0);   // include_directories  (empty)
 
 	files = emallocz(ctxt->nhistfile*sizeof files[0]);

コアとなるコードの解説

  1. OPCODE_BASE の変更: enum 定義内で、OPCODE_BASE の値が 5 から 10 に変更されています。

    -	OPCODE_BASE = 5
    +	OPCODE_BASE = 10
    

    これは、DWARFラインナンバープログラムのヘッダに書き込まれるオペコードベースの値を直接変更するものです。これにより、特別なオペコードが始まるオフセットが調整され、DWARF 2仕様で定義されている9つの標準オペコード(1から9)がすべて、標準オペコードとして正しく認識されるようになります。

  2. standard_opcode_lengths の初期化の拡張: writelines 関数内で、standard_opcode_lengths 配列の初期化部分が変更されています。以前は標準オペコード1から4までの長さしか記述されていませんでしたが、OPCODE_BASEが10になったことに伴い、標準オペコード5から9までの長さも追加で記述されるようになりました。

    	cput(1);   // standard_opcode_lengths[1]
    	cput(1);   // standard_opcode_lengths[2]
    	cput(1);   // standard_opcode_lengths[3]
    	cput(1);   // standard_opcode_lengths[4]
    +	cput(1);   // standard_opcode_lengths[5]
    +	cput(0);   // standard_opcode_lengths[6]
    +	cput(0);   // standard_opcode_lengths[7]
    +	cput(0);   // standard_opcode_lengths[8]
    +	cput(1);   // standard_opcode_lengths[9]
    

    standard_opcode_lengthsは、各標準オペコードが取るオペランドの数を定義する配列です。この変更により、Goリンカが生成するDWARFラインナンバープログラムのヘッダが、DWARF 2仕様に完全に準拠し、他のDWARFツールがより正確にデバッグ情報を解析できるようになります。特に、standard_opcode_lengths[5]standard_opcode_lengths[9]が1に設定されていることから、これらのオペコードが1つのオペランドを取ることが示唆されます。他のオペコード(6, 7, 8)が0に設定されているのは、それらがオペランドを取らないか、Goのコンテキストでは使用されないことを意味する可能性があります。

関連リンク

参考にした情報源リンク