[インデックス 10164] ファイルの概要
このコミットは、Go言語のビルドシステムにおけるアーカイブファイル(.a
ファイル)内のファイル名の長さを、以前の64バイトから16バイトに戻す変更です。これは、Goのコンパイルプロセスが進化し、個々のGoソースファイルがアーカイブ内で一意の名前を持つ必要がなくなったこと、およびPlan 9オペレーティングシステムとの互換性を改善するために行われました。
コミット
commit 7b04471dfaaddc49efad470275fcc45546870a73
Author: Russ Cox <rsc@golang.org>
Date: Tue Nov 1 00:29:16 2011 -0400
gopack: change archive file name length back to 16
This CL grew the archive file name length from 16 to 64:
changeset: 909:58574851d792
user: Russ Cox <rsc@golang.org>
date: Mon Oct 20 13:53:56 2008 -0700
Back then, every x.go file in a package became an x.6 file
in the archive. It was important to be able to allow the
use of long Go source file names, hence the increase in size.
Today, all Go source files compile into a single _go_.6 file
regardless of their names, so the archive file name length
no longer needs to be long. The longer name causes some
problems on Plan 9, where the native archive format is the
same but with 16-byte names, so revert back to 16.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5333050
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/7b04471dfaaddc49efad470275fcc45546870a73
元コミット内容
このコミットは、Goのビルドツールであるgopack
が生成するアーカイブファイル(通常は.a
拡張子を持つ静的ライブラリ)内のファイル名(メンバー名)の長さを、64バイトから16バイトに戻すものです。
コミットメッセージによると、以前(2008年10月20日の変更セット909:58574851d792)は、パッケージ内の各.go
ファイルがアーカイブ内でx.6
のような個別のファイルとして扱われていたため、長いGoソースファイル名をサポートするためにファイル名の長さを16バイトから64バイトに拡張する必要がありました。
しかし、このコミットが作成された時点(2011年11月)では、Goのコンパイルプロセスが変更され、すべてのGoソースファイルがパッケージ内で単一の_go_.6
ファイルにコンパイルされるようになりました。この変更により、個々のソースファイル名がアーカイブ内のメンバー名として直接使用される必要がなくなり、ファイル名の長さを長く保つ必要がなくなりました。
さらに、ファイル名の長さが長いと、Plan 9オペレーティングシステム上で問題が発生することが判明しました。Plan 9のネイティブアーカイブフォーマットはGoが使用しているものと似ていますが、ファイル名の長さが16バイトに制限されています。この互換性の問題を解決するためにも、ファイル名の長さを16バイトに戻すことが決定されました。
変更の背景
この変更の背景には、主に以下の2つの要因があります。
-
Goコンパイルプロセスの進化: Go言語の初期段階では、パッケージ内の各Goソースファイル(例:
foo.go
,bar.go
)が、コンパイル後にそれぞれfoo.6
,bar.6
といった形で個別のオブジェクトファイルとしてアーカイブ(静的ライブラリ)に格納されていました。このため、開発者が長いファイル名を使用した場合でも、それらがアーカイブ内で適切に表現されるように、アーカイブメンバー名の長さを十分に確保する必要がありました。これが、以前にファイル名長を16バイトから64バイトに拡張した理由です。 しかし、このコミットがなされた時期には、Goのビルドシステムが進化し、パッケージ内のすべてのGoソースファイルがコンパイルされて、最終的に単一の_go_.6
という名前のオブジェクトファイルとしてアーカイブに格納されるようになりました。これにより、個々のGoソースファイル名がアーカイブ内のメンバー名として直接使用される必要がなくなり、長いファイル名をサポートするための64バイトの長さが不要になりました。 -
Plan 9との互換性: Go言語は、その設計思想や開発チームのルーツにおいて、Bell LabsのPlan 9オペレーティングシステムから大きな影響を受けています。そのため、Goのツールチェインや内部フォーマットには、Plan 9の慣習やフォーマットが採用されている部分が多くあります。
ar
アーカイブフォーマットもその一つです。Plan 9のネイティブなar
フォーマットでは、アーカイブメンバー名の長さが16バイトに固定されています。Goのgopack
が64バイトのファイル名を使用していると、Plan 9環境でGoの生成したアーカイブファイルを処理する際に互換性の問題が生じました。この問題を解消し、Plan 9上でのGoのツールチェインの動作を円滑にするために、ファイル名の長さをPlan 9の標準に合わせて16バイトに戻す必要がありました。
これらの理由から、不要になった長いファイル名長を元に戻し、同時にPlan 9との互換性を確保することが、このコミットの目的となりました。
前提知識の解説
1. Go言語の初期のビルドプロセスとgopack
Go言語の初期のビルドシステムでは、gopack
というツールが重要な役割を担っていました。gopack
は、Goのソースファイルをコンパイルして生成されたオブジェクトファイル(.6
拡張子を持つファイル)を、Unix系のシステムで広く使われているar
(archiver)フォーマットのアーカイブファイル(通常は.a
拡張子を持つ静的ライブラリ)にまとめるためのツールでした。
当時のGoのコンパイルフローは、おおよそ以下のようでした。
go tool 6g
(Goコンパイラ)が.go
ソースファイルをコンパイルし、.6
オブジェクトファイルを生成する。go tool pack
(gopack
)が、これらの.6
オブジェクトファイルをまとめて.a
アーカイブファイルを生成する。go tool 6l
(Goリンカ)が、これらの.a
アーカイブファイルと他のライブラリをリンクして実行可能ファイルを生成する。
このコミットの時点では、gopack
はまだGoのビルドプロセスの一部として機能していましたが、後にgo build
コマンドの内部処理に統合され、ユーザーが直接意識することはなくなりました。
2. ar
アーカイブフォーマット
ar
(archiver)は、複数のファイルを単一のアーカイブファイルにまとめるためのUnix系のユーティリティおよびファイルフォーマットです。主に静的ライブラリ(.a
ファイル)の作成に使用されます。ar
アーカイブは、ヘッダとそれに続くメンバー(アーカイブに格納された個々のファイル)で構成されます。
各メンバーは、そのファイル名、サイズ、パーミッション、タイムスタンプなどの情報を含む独自のヘッダを持っています。このコミットで問題となっているのは、このメンバーヘッダ内の「ファイル名」フィールドの長さです。
標準的なar
フォーマット(特にBSDやSystem Vのバリアント)では、メンバー名のフィールド長は通常16バイトに制限されています。長いファイル名を格納するためには、特別な拡張(例えば、ファイル名が16バイトを超える場合は、そのファイル名をアーカイブの末尾に別途格納し、ヘッダのファイル名フィールドにはそのオフセットを記述する)が必要になることがあります。
3. Plan 9オペレーティングシステム
Plan 9 from Bell Labsは、Unixの後継として設計された分散オペレーティングシステムです。ファイルシステムを中心とした設計思想、UTF-8の採用、9P
プロトコルによるネットワーク透過性など、多くの革新的なアイデアを導入しました。Go言語の開発者の一部はPlan 9の開発にも携わっており、Go言語の設計にはPlan 9の哲学が色濃く反映されています。
Plan 9のツールチェインやファイルフォーマットは、Goの初期のツールチェインに大きな影響を与えました。特に、Goのオブジェクトファイル(.6
)やアーカイブファイル(.a
)のフォーマットは、Plan 9のそれと非常に似ています。このコミットで言及されているように、Plan 9のネイティブなar
フォーマットは、ファイル名の長さに16バイトの制限を持っています。
4. Goのコンパイルにおける_go_.6
ファイル
Go言語のコンパイルプロセスは、時間の経過とともに進化してきました。初期のGoでは、パッケージ内の各Goソースファイル(例: main.go
, utils.go
)が個別にコンパイルされ、それぞれmain.6
, utils.6
といったオブジェクトファイルが生成され、これらがgopack
によってアーカイブにまとめられていました。この方式では、アーカイブ内の各メンバーが元のGoソースファイル名に対応するため、長いソースファイル名をサポートするためにアーカイブメンバー名の長さを長くする必要がありました。
しかし、このコミットの時点では、Goのコンパイル戦略が変更されていました。現在では、パッケージ内のすべてのGoソースファイルは、コンパイル時にまとめて処理され、最終的に単一のオブジェクトファイル(通常は_go_.6
という名前)としてアーカイブに格納されます。この_go_.6
ファイルには、パッケージ内のすべてのGoソースコードから生成されたコンパイル済みコードが含まれます。
この変更により、アーカイブ内のメンバー名は常に_go_.6
となるため、元のGoソースファイル名が何であっても、アーカイブメンバー名の長さを長く保つ必要がなくなりました。これは、アーカイブフォーマットの設計を簡素化し、互換性の問題を解決する上で重要な変更でした。
技術的詳細
このコミットの技術的詳細は、主にar
アーカイブフォーマットのヘッダ構造と、Goのツールチェインがそのヘッダをどのように解釈・生成するかに焦点を当てています。
ar
アーカイブの各メンバーは、ar_hdr
という構造体で定義されるヘッダを持ちます。このヘッダには、メンバーのファイル名、サイズ、所有者、グループ、パーミッション、最終更新日時などのメタデータが含まれます。
このコミットで変更されたのは、include/ar.h
で定義されているSARNAME
マクロの値です。
- 変更前:
#define SARNAME 64
- 変更後:
#define SARNAME 16
SARNAME
は、ar_hdr
構造体内のファイル名フィールドの長さを定義しています。この値を64から16に戻すことで、Goが生成するアーカイブファイルのメンバー名フィールドが16バイトに制限されるようになります。
この変更は、Goのリンカ(src/cmd/ld/lib.c
)と、実験的な型システムのエクスポートデータ処理(src/pkg/exp/types/exportdata.go
)に影響を与えます。
src/cmd/ld/lib.c
における変更
src/cmd/ld/lib.c
のnextar
関数は、アーカイブファイルから次のメンバーヘッダを読み込む役割を担っています。この関数は、読み込んだヘッダのサイズに基づいて、それが標準的なヘッダ(SAR_HDR
)であるか、あるいは古いPlan 9形式のヘッダであるかを判断していました。
変更前は、SARNAME
が64バイトであったため、SAR_HDR
のサイズもそれに応じて大きくなっていました。古いPlan 9のアーカイブは16バイトのファイル名を使用していたため、Goのリンカは、読み込んだヘッダのサイズがSAR_HDR-SARNAME+16
(つまり、Goの64バイト名ヘッダから48バイト減らしたサイズ)である場合に、それを古いPlan 9形式のヘッダとして特別に処理していました。この処理には、ヘッダ内のファイル名部分を適切にコピーし直すロジックが含まれていました。
変更後、SARNAME
が16バイトに戻されたことで、Goが生成するアーカイブのヘッダサイズはPlan 9の標準と一致するようになりました。これにより、古いPlan 9形式のヘッダを特別に扱う必要がなくなり、nextar
関数内の条件分岐と複雑なmemmove
処理が削除され、コードが大幅に簡素化されました。リンカは、ヘッダのサイズがSAR_HDR
と一致することだけを確認すればよくなりました。
src/pkg/exp/types/exportdata.go
における変更
src/pkg/exp/types/exportdata.go
のreadGopackHeader
関数は、gopack
が生成するアーカイブのヘッダを読み込み、その中のパッケージ名とサイズを抽出するために使用されていました。この関数は、ヘッダの特定のオフセットからファイル名やサイズ情報を読み取るために、SARNAME
の値に依存していました。
変更前は、ヘッダの読み取りバッファサイズや、ファイル名とサイズ情報を抽出するためのオフセット計算に64
という値がハードコードされていました。これは、以前のSARNAME
の値(64)に対応していました。
変更後、SARNAME
が16バイトに戻されたことに合わせて、これらのハードコードされた64
という値が16
に修正されました。これにより、readGopackHeader
関数が新しい16バイトのファイル名長を持つアーカイブヘッダを正しく解析できるようになります。具体的には、ヘッダのバッファサイズが小さくなり、ファイル名とサイズ情報の開始オフセットが調整されました。
これらの変更は、Goのビルドシステム全体でar
アーカイブフォーマットのファイル名長に関する一貫性を保ち、特にPlan 9との互換性を確保するために不可欠でした。
コアとなるコードの変更箇所
このコミットにおける主要なコード変更は、以下の3つのファイルにわたります。
-
include/ar.h
:--- a/include/ar.h +++ b/include/ar.h @@ -32,7 +32,7 @@ #define SARMAG 8 #define ARFMAG "`\n" -#define SARNAME 64 +#define SARNAME 16 struct ar_hdr {
SARNAME
マクロの定義が64
から16
に変更されました。これは、アーカイブメンバー名の最大長を定義する最も根本的な変更です。
-
src/cmd/ld/lib.c
:--- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -313,15 +313,9 @@ nextar(Biobuf *bp, int off, struct ar_hdr *a) return 0; return -1; } - if(r == SAR_HDR) { - memmove(a, buf, SAR_HDR); - } else if (r == SAR_HDR-SARNAME+16) { // old Plan 9 - memset(a->name, ' ', sizeof a->name); - memmove(a, buf, 16); - memmove((char*)a+SARNAME, buf+16, SAR_HDR-SARNAME); - } else { // unexpected + if(r != SAR_HDR) return -1; - } + memmove(a, buf, SAR_HDR); if(strncmp(a->fmag, ARFMAG, sizeof a->fmag))\ return -1; arsize = strtol(a->size, 0, 0);
nextar
関数内の、アーカイブヘッダの読み込みと解析ロジックが簡素化されました。- 以前は、読み込んだヘッダのサイズが
SAR_HDR
と一致する場合と、SAR_HDR-SARNAME+16
(古いPlan 9形式)と一致する場合で異なる処理を行っていましたが、この複雑な条件分岐が削除されました。 - 新しいコードでは、読み込んだサイズ
r
がSAR_HDR
と一致しない場合はエラーとし、一致する場合は単純にmemmove(a, buf, SAR_HDR)
でヘッダをコピーするだけになりました。これは、SARNAME
が16バイトに戻ったことで、Goが生成するヘッダとPlan 9のヘッダが同じ形式になったため、特別な処理が不要になったことを示しています。
-
src/pkg/exp/types/exportdata.go
:--- a/src/pkg/exp/types/exportdata.go +++ b/src/pkg/exp/types/exportdata.go @@ -17,7 +17,7 @@ import ( func readGopackHeader(buf *bufio.Reader) (name string, size int, err os.Error) { // See $GOROOT/include/ar.h. - hdr := make([]byte, 64+12+6+6+8+10+2) + hdr := make([]byte, 16+12+6+6+8+10+2) _, err = io.ReadFull(buf, hdr) if err != nil { return @@ -25,13 +25,13 @@ func readGopackHeader(buf *bufio.Reader) (name string, size int, err os.Error) { if trace { fmt.Printf("header: %s", hdr) } - s := strings.TrimSpace(string(hdr[64+12+6+6+8:][:10])) + s := strings.TrimSpace(string(hdr[16+12+6+6+8:][:10])) size, err = strconv.Atoi(s) if err != nil || hdr[len(hdr)-2] != '`' || hdr[len(hdr)-1] != '\n' { err = os.NewError("invalid archive header") return } - name = strings.TrimSpace(string(hdr[:64])) + name = strings.TrimSpace(string(hdr[:16])) return }
readGopackHeader
関数内で、アーカイブヘッダを読み込むためのバッファサイズと、ヘッダ内のファイル名およびサイズ情報を抽出するためのオフセット計算が修正されました。make([]byte, 64+...)
がmake([]byte, 16+...)
に変更され、ヘッダの読み取りサイズがSARNAME
の新しい値に合わせて調整されました。hdr[64+12+6+6+8:][:10]
がhdr[16+12+6+6+8:][:10]
に変更され、サイズ情報のオフセットが調整されました。hdr[:64]
がhdr[:16]
に変更され、ファイル名(name
)を抽出する際の範囲が調整されました。
コアとなるコードの解説
include/ar.h
の変更
SARNAME
は、ar
アーカイブフォーマットのヘッダ構造体ar_hdr
内で、メンバーのファイル名を格納するフィールドの長さを定義しています。この値を64から16に変更することは、Goが生成するアーカイブファイルが、ファイル名に最大16バイトしか使用しないことを意味します。これは、Plan 9のar
フォーマットの標準的なファイル名長と一致します。
src/cmd/ld/lib.c
の変更
nextar
関数は、Goのリンカが静的ライブラリ(.a
ファイル)を読み込む際に、その中の個々のオブジェクトファイル(メンバー)のヘッダを解析するために使用されます。
変更前のコードは、Goが以前に64バイトのファイル名長を使用していた時期の名残です。この時期、Goのリンカは、Plan 9のシステムで生成された可能性のある16バイトのファイル名長を持つ古いアーカイブも処理できるように、特別なロジックを持っていました。具体的には、読み込んだヘッダのサイズがGoの標準(SAR_HDR
)と異なる場合(特にSAR_HDR-SARNAME+16
、つまりGoの64バイト名ヘッダから48バイト減らしたサイズ)は、それを古いPlan 9形式のヘッダとみなし、ファイル名部分を適切に調整してコピーしていました。
SARNAME
が16バイトに戻されたことで、Goが生成するアーカイブのヘッダ構造はPlan 9の標準と完全に一致するようになりました。これにより、リンカはもはや「古いPlan 9形式」のヘッダを特別扱いする必要がなくなりました。読み込んだヘッダのサイズがSAR_HDR
(新しい16バイト名に対応したサイズ)と一致することだけを確認すればよくなり、コードが大幅に簡素化され、保守性が向上しました。これは、GoのツールチェインがPlan 9のar
フォーマットと完全に互換性を持つようになったことを示しています。
src/pkg/exp/types/exportdata.go
の変更
readGopackHeader
関数は、Goのパッケージのエクスポートデータ(他のパッケージから参照可能な型や関数などの情報)を含むアーカイブのヘッダを読み取るために使用されます。この関数は、ヘッダのバイト列からファイル名(パッケージ名)とサイズ情報を抽出します。
変更前のコードでは、ヘッダのバッファサイズや、ファイル名とサイズ情報を抽出するためのオフセット計算に、以前のSARNAME
の値である64
がハードコードされていました。これは、ヘッダの構造が64バイトのファイル名フィールドを前提としていたためです。
SARNAME
が16バイトに変更されたことに伴い、この関数内のハードコードされた64
という値が16
に修正されました。これにより、readGopackHeader
は、新しい16バイトのファイル名長を持つアーカイブヘッダから、正しいオフセットでパッケージ名とサイズ情報を正確に読み取ることができるようになりました。この変更は、Goのビルドシステム全体でアーカイブフォーマットの定義と解析ロジックの一貫性を保つために必要でした。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/7b04471dfaaddc49efad470275fcc45546870a73
- Go Code Review (CL): https://golang.org/cl/5333050
参考にした情報源リンク
ar
(Unix): https://en.wikipedia.org/wiki/Ar_(Unix)- Plan 9 from Bell Labs: https://en.wikipedia.org/wiki/Plan_9_from_Bell_Labs
- Go言語の初期のビルドプロセスに関する議論やドキュメント: (特定のURLは提供できませんが、Goの公式ブログやメーリングリストのアーカイブ、初期のGoのソースコードリポジトリなどを参照しました。)
- Go言語のコンパイルとパッケージングに関する情報: (Goの公式ドキュメントやGoのソースコード内の関連ファイルを参照しました。)
gopack
に関する情報: (Goの初期のツールチェインに関するドキュメントやソースコードを参照しました。)_go_.6
ファイルに関する情報: (Goのコンパイルプロセスに関する技術記事やGoのソースコードを参照しました。)ar.h
ヘッダファイル: (Unix系システムの標準的なar.h
ファイルや、Goのソースコード内のinclude/ar.h
を参照しました。)src/cmd/ld/lib.c
: (Goのリンカのソースコードを参照しました。)src/pkg/exp/types/exportdata.go
: (Goの実験的な型システムのエクスポートデータ処理に関するソースコードを参照しました。)- Russ Coxの過去のコミット: (コミットメッセージで言及されている変更セット909:58574851d792のコミットログを参照しました。)