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

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

このコミットは、Go言語のツールチェーンにおいて、アーカイブファイル内のシンボル定義セクションの識別子を __.SYMDEF から __.GOSYMDEF へと変更するものです。この変更の主な目的は、標準的なELF(Executable and Linkable Format)ツールがGoの非標準的なシンボルテーブルフォーマットを誤って解釈する問題を解決することにあります。これにより、Goのツールチェーンが生成するバイナリと、一般的なシステムツールとの間の互換性の問題が解消されます。

コミット

commit 6ee91ced92fc27f8c93b0589484923593cad240f
Author: Russ Cox <rsc@golang.org>
Date:   Thu Sep 13 10:26:21 2012 -0400

    cmd/pack: rename __.SYMDEF to __.GOSYMDEF
    
    This fixes a problem with ELF tools thinking they know the
    format of the symbol table, as we do not use any of the
    standard formats for that table.
    
    This change will probably annoy the Plan 9 users, but I
    believe there are other incompatibilities already that mean
    they have to use a Go-specific nm.
    
    Fixes #3473.
    
    R=golang-dev, iant
    CC=golang-dev
    https://golang.org/cl/6500117

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

https://github.com/golang/go/commit/6ee91ced92fc27f8c93b0589484923593cad240f

元コミット内容

commit 6ee91ced92fc27f8c93b0589484923593cad240f
Author: Russ Cox <rsc@golang.org>
Date:   Thu Sep 13 10:26:21 2012 -0400

    cmd/pack: rename __.SYMDEF to __.GOSYMDEF
    
    This fixes a problem with ELF tools thinking they know the
    format of the symbol table, as we do not use any of the
    standard formats for that table.
    
    This change will probably annoy the Plan 9 users, but I
    believe there are other incompatibilities already that mean
    they have to use a Go-specific nm.
    
    Fixes #3473.
    
    R=golang-dev, iant
    CC=golang-dev
    https://golang.org/cl/6500117

変更の背景

この変更は、Go言語のコンパイラやリンカが生成するアーカイブファイル(主にライブラリファイル)内のシンボルテーブルの扱いに関する問題を解決するために行われました。

Goのツールチェーンは、その内部で独自のシンボルテーブルフォーマットを使用しています。しかし、このシンボルテーブルがアーカイブファイル内で __.SYMDEF という名前で識別されていたため、LinuxなどのUnix系システムで広く使われているELF(Executable and Linkable Format)形式を扱う標準的なツール(例えば nm コマンドなど)が、この __.SYMDEF を標準的なシンボル定義セクションとして誤って認識してしまう問題が発生していました。

標準ツールがGoの非標準フォーマットを標準として扱おうとすると、解析エラーや予期せぬ動作を引き起こす可能性がありました。この問題を回避するため、Go独自のシンボル定義セクションの識別子を __.SYMDEF から __.GOSYMDEF へと変更することで、名前の衝突を避け、標準ツールがGoのシンボルテーブルを誤解釈するのを防ぐことが目的です。

コミットメッセージには「Fixes #3473」とあり、これはGoのIssueトラッカーにおけるIssue 3473を修正するものであることを示しています。このIssueは、まさに __.SYMDEF が原因で nm などのツールがGoのアーカイブファイルを正しく扱えないという問題が報告されていました。

また、コミットメッセージではPlan 9ユーザーへの影響についても言及されています。Go言語はPlan 9オペレーティングシステムの影響を強く受けており、一部のGoツールはPlan 9の慣習に従っています。__.SYMDEF はPlan 9のアーカイブフォーマットにおけるシンボル定義の慣習的な名前でした。しかし、GoのシンボルテーブルはPlan 9のそれとは異なるため、Go固有の nm ツールを使用する必要があるという現状を鑑み、互換性よりも問題解決を優先した形です。

前提知識の解説

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

  1. ELF (Executable and Linkable Format):

    • LinuxやUnix系OSで実行ファイル、オブジェクトファイル、共有ライブラリなどのバイナリファイルを格納するための標準的なファイルフォーマットです。
    • ELFファイルは、プログラムコード、データ、シンボルテーブル、デバッグ情報など、様々なセクションで構成されます。
    • nm などのツールは、ELFファイルの構造を解析して情報を抽出します。
  2. シンボルテーブル (Symbol Table):

    • コンパイルされたプログラムやライブラリ内に含まれるデータ構造の一つです。
    • 関数名、グローバル変数名、静的変数名などの「シンボル」と、それらがメモリ上のどこに配置されているか(アドレス)のマッピング情報を含みます。
    • リンカはシンボルテーブルを使用して、異なるオブジェクトファイルやライブラリ間の参照を解決します。デバッガやプロファイラもシンボルテーブルを利用して、人間が読める形式で情報を提供します。
  3. nm コマンド:

    • Unix系OSで利用されるコマンドラインツールで、オブジェクトファイル、アーカイブファイル、共有ライブラリなどのバイナリファイルからシンボル(関数名や変数名など)のリストを表示するために使用されます。
    • シンボルの種類(関数、変数、未定義など)やアドレスなどの情報を提供します。
  4. アーカイブファイル (Archive File):

    • 複数のオブジェクトファイルやライブラリを一つにまとめたファイル形式です。Unix系では .a 拡張子を持つ静的ライブラリがこれに該当します。
    • リンカはアーカイブファイルから必要なオブジェクトファイルを選択的にリンクします。
    • アーカイブファイルには、通常、内部のオブジェクトファイルに含まれるシンボルを効率的に検索するためのインデックス(シンボルテーブル)が含まれています。
  5. Go言語のツールチェーン:

    • Go言語のプログラムをビルドするために使用される一連のツール群です。主要なものには以下があります。
      • go tool compile (gc): Goソースコードをオブジェクトファイルにコンパイルします。
      • go tool link (ld, 5l, 6l, 8l): オブジェクトファイルをリンクして実行可能ファイルやライブラリを生成します。5l, 6l, 8l はそれぞれARM、x86-64、x86アーキテクチャ向けのリンカです。
      • go tool pack (ar): Goのアーカイブファイルを操作するツールです。Unixの ar コマンドに似ています。
      • go tool nm: Goのバイナリファイルからシンボルをリスト表示するGo固有の nm ツールです。
  6. Plan 9:

    • ベル研究所で開発された分散オペレーティングシステムです。Go言語の設計思想や一部のツールはPlan 9の影響を強く受けています。
    • Plan 9のアーカイブフォーマットでは、シンボル定義セクションが __.SYMDEF という名前で識別されるのが一般的でした。

技術的詳細

このコミットの技術的な核心は、Goのツールチェーンが生成するアーカイブファイル内のシンボルテーブルの識別子を、既存の __.SYMDEF から __.GOSYMDEF へと変更することにあります。

Goのコンパイラやリンカは、標準的なELFフォーマットとは異なる独自の内部フォーマットでシンボル情報を管理しています。これはGoのクロスコンパイル能力や、特定のランタイム要件(例: ガベージコレクション)に対応するために設計されたものです。

問題は、Goが生成するアーカイブファイルが、その内部に __.SYMDEF という名前の特別なエントリを含んでいたことです。この __.SYMDEF は、Plan 9のアーカイブフォーマットにおけるシンボルテーブルの慣習的な名前と一致していました。しかし、Goの __.SYMDEF の内容はPlan 9や一般的なELFツールの期待するフォーマットとは全く異なっていました。

結果として、Linuxなどのシステムで nm のような標準的なELFツールがGoのアーカイブファイルを解析しようとすると、__.SYMDEF という名前を見つけて「これはシンボルテーブルだ」と判断し、その内容を標準的なシンボルテーブルフォーマットとして解釈しようとしました。しかし、フォーマットが異なるため、ツールは解析に失敗したり、誤った情報を表示したり、クラッシュしたりする可能性がありました。

この問題を解決するために、Goのツールチェーンはシンボルテーブルの識別子を __.GOSYMDEF に変更しました。この新しい名前は、Go固有のシンボル定義であることを明確に示し、標準的なELFツールがこれを一般的なシンボルテーブルとして誤解釈するのを防ぎます。これにより、Goのアーカイブファイルがシステム上の他のツールと共存しやすくなります。

src/pkg/exp/types/exportdata.go の変更は特に重要です。このファイルはGoのパッケージのエクスポートデータを処理する部分であり、古いアーカイブファイルとの互換性を考慮しています。具体的には、FindGcExportData 関数内で、アーカイブの最初のエントリが __.SYMDEF または __.GOSYMDEF のいずれかであることを許容するように変更されています。これにより、この変更が適用される前にビルドされたGoのアーカイブファイルも引き続き正しく処理できるようになっています。

この変更は、Goのツールチェーンが生成するバイナリの「自己完結性」を保ちつつ、外部ツールとの不必要な衝突を避けるための、実用的な解決策と言えます。

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

このコミットでは、主にGoのリンカ、コンパイラ、アーカイブツール、および関連するユーティリティが使用するシンボル定義の文字列定数が変更されています。

  1. リンカのヘッダーファイル (src/cmd/5l/5.out.h, src/cmd/6l/6.out.h, src/cmd/8l/8.out.h): これらのファイルは、それぞれ異なるアーキテクチャ(ARM, x86-64, x86)向けのGoリンカが使用する定数を定義しています。SYMDEF マクロの定義が変更されています。

    --- a/src/cmd/5l/5.out.h
    +++ b/src/cmd/5l/5.out.h
    @@ -276,7 +276,7 @@ enum
     /*
      * this is the ranlib header
      */
    -#define	SYMDEF	"__.SYMDEF"
    +#define	SYMDEF	"__.GOSYMDEF"
     
     /*
      * this is the simulated IEEE floating point
    

    同様の変更が 6l/6.out.h8l/8.out.h にも適用されています。

  2. コンパイラの字句解析部分 (src/cmd/gc/lex.c): Goコンパイラの一部で、アーカイブファイルを読み込む際にシンボルテーブルをスキップするロジックが変更されています。

    --- a/src/cmd/gc/lex.c
    +++ b/src/cmd/gc/lex.c
    @@ -480,7 +480,7 @@ skiptopkgdef(Biobuf *b)
     	if(memcmp(p, "!<arch>\\n", 8) != 0)
     		return 0;
     	/* symbol table is first; skip it */
    -	sz = arsize(b, "__.SYMDEF");
    +	sz = arsize(b, "__.GOSYMDEF");
     	if(sz < 0)
     		return 0;
     	Bseek(b, sz, 1);
    
  3. リンカのライブラリ処理部分 (src/cmd/ld/lib.c): Goリンカがライブラリ(アーカイブファイル)を処理する際に、__.SYMDEF セクションをスキップする箇所が変更されています。

    --- a/src/cmd/ld/lib.c
    +++ b/src/cmd/ld/lib.c
    @@ -366,7 +366,7 @@ objfile(char *file, char *pkg)
     		return;
     	}
     	
    -	/* skip over __.SYMDEF */
    +	/* skip over __.GOSYMDEF */
     	off = Boffset(f);
     	if((l = nextar(f, off, &arhdr)) <= 0) {
     		diag("%s: short read on archive file symbol header", file);
    @@ -402,7 +402,7 @@ objfile(char *file, char *pkg)
      * the individual symbols that are unused.
      *
      * loading every object will also make it possible to
    - * load foreign objects not referenced by __.SYMDEF.
    + * load foreign objects not referenced by __.GOSYMDEF.
      */
     	for(;;) {
     		l = nextar(f, off, &arhdr);
    
  4. nm コマンドのソース (src/cmd/nm/nm.c): Goの nm ツールが使用するシンボルテーブルファイル名の定数が変更されています。

    --- a/src/cmd/nm/nm.c
    +++ b/src/cmd/nm/nm.c
    @@ -43,7 +43,7 @@ enum{
     
     char	*errs;			/* exit status */
     char	*filename;		/* current file */
    -char	symname[]="__.SYMDEF";	/* table of contents file name */
    +char	symname[]="__.GOSYMDEF";	/* table of contents file name */
     int	multifile;		/* processing multiple files */
     int	aflag;
     int	gflag;
    
  5. pack コマンドのアーカイブ処理部分 (src/cmd/pack/ar.c): Goのアーカイブツール pack が使用するシンボル定義ファイル名の定数が変更されています。

    --- a/src/cmd/pack/ar.c
    +++ b/src/cmd/pack/ar.c
    @@ -111,7 +111,7 @@ char	*opt =		"uvnbailogS";
     char	artemp[] =	"/tmp/vXXXXX";
     char	movtemp[] =	"/tmp/v1XXXXX";
     char	tailtemp[] =	"/tmp/v2XXXXX";
    -char	symdef[] =	"__.SYMDEF";
    +char	symdef[] =	"__.GOSYMDEF";
     char	pkgdef[] =	"__.PKGDEF";
     
     int	aflag;				/* command line flags */
    
  6. Goパッケージのエクスポートデータ処理 (src/pkg/exp/types/exportdata.go): Goのパッケージエクスポートデータを読み込む際に、古い __.SYMDEF と新しい __.GOSYMDEF の両方を許容するように変更されています。

    --- a/src/pkg/exp/types/exportdata.go
    +++ b/src/pkg/exp/types/exportdata.go
    @@ -52,13 +52,14 @@ func FindGcExportData(r *bufio.Reader) (err error) {
     		var name string
     		var size int
     
    -		// First entry should be __.SYMDEF.
    +		// First entry should be __.GOSYMDEF.
    +		// Older archives used __.SYMDEF, so allow that too.
     		// Read and discard.
     		if name, size, err = readGopackHeader(r); err != nil {
     			return
     		}
    -		if name != "__.SYMDEF" {
    -			err = errors.New("go archive does not begin with __.SYMDEF")
    +		if name != "__.SYMDEF" && name != "__.GOSYMDEF" {
    +			err = errors.New("go archive does not begin with __.SYMDEF or __.GOSYMDEF")
     			return
     		}
     		const block = 4096
    

コアとなるコードの解説

上記の変更は、Goのビルドシステム全体で __.SYMDEF という文字列定数を __.GOSYMDEF に置き換えることを目的としています。

  • リンカのヘッダーファイル (.out.h): これらのファイルは、Goのリンカがオブジェクトファイルやアーカイブファイルを処理する際に使用する内部的な定数を定義しています。SYMDEF マクロは、アーカイブファイル内のシンボルテーブルセクションを識別するために使われる文字列を定義していました。この変更により、リンカが新しい識別子 __.GOSYMDEF を使用してシンボルテーブルを生成・認識するようになります。

  • コンパイラの字句解析部分 (src/cmd/gc/lex.c): skiptopkgdef 関数は、パッケージ定義をスキップする際に、アーカイブの先頭にあるシンボルテーブルを読み飛ばす処理を行っています。ここで __.SYMDEF を検索してそのサイズを取得していましたが、この検索対象が __.GOSYMDEF に変更されました。これにより、コンパイラが新しい識別子を持つアーカイブを正しく処理できるようになります。

  • リンカのライブラリ処理部分 (src/cmd/ld/lib.c): objfile 関数は、オブジェクトファイルやライブラリファイルを処理するGoリンカの主要な部分です。この関数内で、アーカイブファイルから __.SYMDEF セクションをスキップするロジックが存在しました。この変更により、リンカは __.GOSYMDEF をスキップするようになります。また、コメントも更新され、__.GOSYMDEF によって参照されない外部オブジェクトのロード可能性についても言及されています。

  • nm コマンドのソース (src/cmd/nm/nm.c): Goの nm ツールは、Goのバイナリファイルからシンボル情報を抽出するために使用されます。symname 変数は、シンボルテーブルのファイル名を定義していました。この変更により、nm ツールは __.GOSYMDEF という名前のセクションをシンボルテーブルとして認識し、解析するようになります。

  • pack コマンドのアーカイブ処理部分 (src/cmd/pack/ar.c): pack コマンドは、Goのオブジェクトファイルをアーカイブファイルにまとめるツールです。symdef 変数は、pack がアーカイブ内にシンボルテーブルを作成する際に使用する名前を定義していました。この変更により、pack は新しい識別子 __.GOSYMDEF を使用してシンボルテーブルを作成するようになります。

  • Goパッケージのエクスポートデータ処理 (src/pkg/exp/types/exportdata.go): FindGcExportData 関数は、Goのパッケージのエクスポートデータを読み込む際に、アーカイブの先頭にあるシンボル定義セクションをチェックします。この変更は、後方互換性を考慮した重要な部分です。新しいコードでは、アーカイブの先頭が __.SYMDEF または __.GOSYMDEF のいずれかであれば許容するようにロジックが変更されました。これにより、このコミットが適用される前にビルドされた古いGoアーカイブファイルも、新しいツールチェーンで引き続き正しく処理できるようになります。これは、既存のGoエコシステムとの互換性を維持するための配慮です。

これらの変更は、Goのビルドシステム全体でシンボルテーブルの識別子を統一し、外部ツールとの衝突を避けるための協調的な作業を示しています。

関連リンク

参考にした情報源リンク