[インデックス 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コンパイラがパッケージを検索し、アーカイブファイルを処理する方法に関する改善を目的としています。
-
__.SYMDEF
の長さが0の場合の許容: 以前のバージョンでは、アーカイブファイル内の__.SYMDEF
セクションのサイズが0の場合、エラーとして扱われていました。しかし、有効なアーカイブファイルの中には、シンボル定義が空であるために__.SYMDEF
のサイズが0になるものも存在します。このようなファイルを正しく処理できるようにするために、サイズが0の場合も許容するように変更されました。 -
.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
ファイル内の以下の関数に影響を与えます。
-
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)
: ヘッダから読み取ったサイズ文字列を整数に変換します。
-
skiptopkgdef(Biobuf *b)
: この関数は、アーカイブファイル内でパッケージ定義のブロックにスキップするために使用されます。具体的には、まず__.SYMDEF
セクションをスキップし、その後にパッケージエクスポートブロックに移動します。__.SYMDEF
のサイズを取得するためにarsize
関数を呼び出していますが、arsize
の戻り値が変更されたことに伴い、if(sz <= 0)
という条件がif(sz < 0)
に変更されました。これは、__.SYMDEF
のサイズが0
である場合を有効なケースとして扱うためです。 -
findpkg(String *name)
: この関数は、指定されたパッケージ名に対応するコンパイル済みパッケージファイル(.6
または.a
)を検索します。 変更の核心は、パッケージファイルの検索順序の変更と、$GOROOT/pkg
ディレクトリ内の検索パスの追加です。-
検索順序の変更: 変更前:
%Z.6
(現在のディレクトリまたは相対パス)%Z.a
(現在のディレクトリまたは相対パス)%s/pkg/%Z.6
($GOROOT/pkg
内)%s/pkg/%Z.a
($GOROOT/pkg
内)
変更後:
%Z.a
(現在のディレクトリまたは相対パス)%Z.6
(現在のディレクトリまたは相対パス)%s/pkg/%Z.6
($GOROOT/pkg
内)%s/pkg/%Z.a
($GOROOT/pkg
内)%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
コマンドのマニュアルページ (アーカイブファイルの構造と操作に関する情報)
参考にした情報源リンク
- Go言語のソースコード (GitHub)
- Unix
ar
コマンド - access(2) - Linux man page
- snprintf(3) - Linux man page
- Goの初期のコンパイル済みファイル形式に関する議論 (GoコミュニティのメーリングリストやIssueトラッカーなど) (具体的なリンクはコミット当時の情報に依存するため、一般的な検索クエリを示します)
- Goのビルドシステムに関するドキュメント