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

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

このコミットは、Goコンパイラのsrc/cmd/gc/lex.cファイルに対する変更です。lex.cは、Goコンパイラのフロントエンドの一部であり、パッケージのインポートやアーカイブファイルの処理に関連するロジックを含んでいると考えられます。具体的には、アーカイブファイル(.aファイル)内のシンボル定義(__.SYMDEF)の扱いと、パッケージファイルの検索順序(.6.a)が変更されています。

コミット

commit 63a84348cc963d4e5d4b7f3e491863889d7b394c
Author: Russ Cox <rsc@golang.org>
Date:   Wed Nov 19 14:21:44 2008 -0800

    look for .6 before .a; accept archives with 0 length __.SYMDEF
    
    TBR=r
    OCL=19612
    CL=19612

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

https://github.com/golang/go/commit/63a84348cc963d4e5d4b7f3e491863889d7b394c

元コミット内容

look for .6 before .a; accept archives with 0 length __.SYMDEF

変更の背景

このコミットは、Goコンパイラがパッケージを検索し、アーカイブファイルを処理する方法に関する改善を目的としています。

  1. __.SYMDEFの長さが0の場合の許容: 以前のバージョンでは、アーカイブファイル内の__.SYMDEFセクションのサイズが0の場合、エラーとして扱われていました。しかし、有効なアーカイブファイルの中には、シンボル定義が空であるために__.SYMDEFのサイズが0になるものも存在します。このようなファイルを正しく処理できるようにするために、サイズが0の場合も許容するように変更されました。

  2. .6.aファイルの検索順序の変更: Goのコンパイル済みパッケージは、通常.6という拡張子を持つファイルとして存在します。一方、.aはUnix系のシステムで一般的なアーカイブファイル(ライブラリファイル)の拡張子です。Goのビルドシステムでは、ライブラリをビルドする際に、複数の.6ファイルをまとめて一つの.aファイルにアーカイブすることがあります。 元のコードでは、パッケージを検索する際に.6ファイルを優先して探していました。しかし、コメントにあるように「ライブラリをビルドする際に重要」なのは、array.aというライブラリの中にarray.6というファイルが含まれている場合、array.6単体ではなく、array.a全体を見つけることでした。これは、ライブラリ全体をリンクする必要があるためです。この変更により、.aファイルを優先して検索することで、ライブラリ全体の依存関係を正しく解決できるようになります。

これらの変更は、Goコンパイラの堅牢性と、ライブラリビルド時の正確性を向上させるために行われました。

前提知識の解説

Go言語のコンパイルとパッケージ

Go言語は、ソースコードをコンパイルして実行可能なバイナリを生成します。Goのコードは「パッケージ」という単位で管理され、他のパッケージをインポートして利用することができます。コンパイルされたパッケージは、通常、特定のディレクトリ(例: $GOROOT/pkg)に配置されます。

Goのコンパイル済みファイル拡張子

  • .6: Go言語のコンパイル済みパッケージファイル(オブジェクトファイル)の拡張子です。これは、Goの特定のバージョン(Go 1.x以前の初期のバージョン)で使われていたもので、現在のGoモジュールシステムでは直接目にすることは少なくなりましたが、内部的には同様の概念が存在します。
  • .a: Unix系のシステムで一般的な「アーカイブファイル」の拡張子です。複数のオブジェクトファイル(この場合は.6ファイル)を一つにまとめたライブラリファイルとして使用されます。arコマンドで作成・管理されます。

アーカイブファイル(.a)と__.SYMDEF

アーカイブファイル(.a)は、複数のファイル(通常はオブジェクトファイル)を一つにまとめたものです。リンカは、これらのアーカイブファイルから必要なオブジェクトファイルを抽出してリンクします。

__.SYMDEFは、アーカイブファイル内に含まれる特殊なメンバー(ファイル)で、アーカイブ内のシンボル定義のインデックス情報を含んでいます。リンカは、この__.SYMDEFを読み込むことで、アーカイブ内のどのオブジェクトファイルにどのシンボルが定義されているかを効率的に検索できます。これにより、アーカイブ全体をスキャンすることなく、必要なシンボルを持つオブジェクトファイルだけを抽出することが可能になります。

access()システムコール

access()は、Unix系のシステムコールの一つで、指定されたファイルやディレクトリに対するユーザーのアクセス権限(読み取り、書き込み、実行)を確認するために使用されます。このコミットでは、ファイルが存在するかどうか(F_OKフラグ、または0)を確認するために使われています。

snprintf()関数

snprintf()は、C言語の標準ライブラリ関数で、書式付きの文字列をバッファに書き込む際に、バッファのサイズを考慮してオーバーフローを防ぐことができる安全な関数です。このコミットでは、ファイルパスを構築するために使用されています。

技術的詳細

このコミットは、Goコンパイラのsrc/cmd/gc/lex.cファイル内の以下の関数に影響を与えます。

  1. arsize(Biobuf *b, char *name): この関数は、アーカイブファイル(.a)内の特定のメンバー(ファイル)のサイズを解析するために使用されます。変更前は、エラー時に0を返していました。しかし、0は有効なサイズである可能性もあるため、エラーを示すために-1を返すように変更されました。これにより、呼び出し元がエラーと有効なサイズ0を区別できるようになります。

    • Brdline(b, '\n'): Biobufから改行までを読み込みます。
    • Blinelen(b): 読み込んだ行の長さを返します。
    • struct ar_hdr: アーカイブメンバーのヘッダ構造体で、メンバー名やサイズなどの情報が含まれます。
    • strncmp(a->name, name, strlen(name)): ヘッダのメンバー名と引数nameを比較します。
    • atoi(a->size): ヘッダから読み取ったサイズ文字列を整数に変換します。
  2. skiptopkgdef(Biobuf *b): この関数は、アーカイブファイル内でパッケージ定義のブロックにスキップするために使用されます。具体的には、まず__.SYMDEFセクションをスキップし、その後にパッケージエクスポートブロックに移動します。 __.SYMDEFのサイズを取得するためにarsize関数を呼び出していますが、arsizeの戻り値が変更されたことに伴い、if(sz <= 0)という条件がif(sz < 0)に変更されました。これは、__.SYMDEFのサイズが0である場合を有効なケースとして扱うためです。

  3. findpkg(String *name): この関数は、指定されたパッケージ名に対応するコンパイル済みパッケージファイル(.6または.a)を検索します。 変更の核心は、パッケージファイルの検索順序の変更と、$GOROOT/pkgディレクトリ内の検索パスの追加です。

    • 検索順序の変更: 変更前:

      1. %Z.6 (現在のディレクトリまたは相対パス)
      2. %Z.a (現在のディレクトリまたは相対パス)
      3. %s/pkg/%Z.6 ($GOROOT/pkg内)
      4. %s/pkg/%Z.a ($GOROOT/pkg内)

      変更後:

      1. %Z.a (現在のディレクトリまたは相対パス)
      2. %Z.6 (現在のディレクトリまたは相対パス)
      3. %s/pkg/%Z.6 ($GOROOT/pkg内)
      4. %s/pkg/%Z.a ($GOROOT/pkg内)
      5. %s/pkg/%Z.6 ($GOROOT/pkg内) - これは重複しているように見えますが、元のコードの%s/pkg/%Z.6の後に%s/pkg/%Z.aが追加され、その後に再度%s/pkg/%Z.6が追加されています。これは、おそらく%Z.6の検索をより優先させるための意図的な重複、またはコードの進化の過程で生じたものかもしれません。しかし、コミットメッセージの意図は.aを優先することなので、この部分の最終的な挙動は注意が必要です。
    • gorootパスの追加: snprintf(namebuf, sizeof(namebuf), "%s/pkg/%Z.a", goroot, name); snprintf(namebuf, sizeof(namebuf), "%s/pkg/%Z.6", goroot, name); これらの行が追加され、$GOROOT/pkgディレクトリ内での.aファイルと.6ファイルの検索が明示的に行われるようになりました。これにより、標準ライブラリやインストールされたパッケージの検索がより確実になります。

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

diff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c
index d305fb65ae..7264b3cad6 100644
--- a/src/cmd/gc/lex.c
+++ b/src/cmd/gc/lex.c
@@ -135,15 +135,16 @@ setfilename(char *file)
 }
 
 int
-arsize(Biobuf *b, char *name){
+arsize(Biobuf *b, char *name)
+{
 	struct ar_hdr *a;
 
 	if((a = Brdline(b, '\n')) == nil)
-\t\treturn 0;\n+\t\treturn -1;
 	if(Blinelen(b) != sizeof(struct ar_hdr))
-\t\treturn 0;\n+\t\treturn -1;
 	if(strncmp(a->name, name, strlen(name)) != 0)
-\t\treturn 0;\n+\t\treturn -1;
 	return atoi(a->size);
 }
 
@@ -162,7 +163,7 @@ skiptopkgdef(Biobuf *b)
 		return 0;
 	/* symbol table is first; skip it */
 	sz = arsize(b, "__.SYMDEF");
-\tif(sz <= 0)\n+\tif(sz < 0)
 		return 0;
 	Bseek(b, sz, 1);
 	/* package export block is second */
@@ -184,16 +185,20 @@ findpkg(String *name)
 	}
 
 	// BOTCH need to get .6 from backend
-\tsnprint(namebuf, sizeof(namebuf), "%Z.6", name);\n-\tif(access(namebuf, 0) >= 0)\n-\t\treturn 1;\n+\n+\t// try .a before .6.  important for building libraries:\n+\t// if there is an array.6 in the array.a library,\n+\t// want to find all of array.a, not just array.6.\n \tsnprint(namebuf, sizeof(namebuf), "%Z.a", name);\n \tif(access(namebuf, 0) >= 0)\n \t\treturn 1;\n-\tsnprint(namebuf, sizeof(namebuf), "%s/pkg/%Z.6", goroot, name);\n+\tsnprint(namebuf, sizeof(namebuf), "%Z.6", name);\n \tif(access(namebuf, 0) >= 0)\n \t\treturn 1;\n \tsnprint(namebuf, sizeof(namebuf), "%s/pkg/%Z.a", goroot, name);\n+\tif(access(namebuf, 0) >= 0)\n+\t\treturn 1;\n+\tsnprint(namebuf, sizeof(namebuf), "%s/pkg/%Z.6", goroot, name);\n \tif(access(namebuf, 0) >= 0)\n \t\treturn 1;\n \treturn 0;

コアとなるコードの解説

arsize関数の変更

  • エラー時の戻り値が0から-1に変更されました。これにより、呼び出し元は0が有効なサイズであることを区別できるようになります。

skiptopkgdef関数の変更

  • arsizeの戻り値の変更に伴い、if(sz <= 0)if(sz < 0)に変更されました。これは、__.SYMDEFのサイズが0である場合を有効なケースとして扱うことを意味します。

findpkg関数の変更

  • 検索順序の変更:

    • 以前は.6ファイルを先に検索していましたが、このコミットにより、現在のディレクトリまたは相対パスでの検索において、.aファイルを先に検索するようになりました。これは、ライブラリ(.a)の中に含まれる個別のパッケージファイル(.6)よりも、ライブラリ全体を優先して見つけるための重要な変更です。
    • コメント「// try .a before .6. important for building libraries: // if there is an array.6 in the array.a library, // want to find all of array.a, not just array.6.」がこの変更の意図を明確に示しています。
  • $GOROOT/pkgパスの追加:

    • $GOROOT/pkgディレクトリ内での.aファイルと.6ファイルの検索パスが追加されました。これにより、Goの標準ライブラリやシステムにインストールされたパッケージの検索がより網羅的になります。

これらの変更により、Goコンパイラはアーカイブファイル内の__.SYMDEFをより柔軟に処理し、ライブラリのビルドとリンクにおいて、より正確なパッケージファイルの検索順序を採用するようになりました。

関連リンク

  • Go言語の公式ドキュメント (Goのパッケージ、コンパイル、ビルドプロセスに関する情報)
  • Unix arコマンドのマニュアルページ (アーカイブファイルの構造と操作に関する情報)

参考にした情報源リンク