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

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

このコミットは、Go言語のリンカ (cmd/ld) において、2GBを超えるサイズのアーカイブファイル(静的ライブラリ)を適切に処理できるようにするための変更です。具体的には、ファイルオフセットを保持する変数の型を、従来の32ビット整数から64ビット整数へと拡張することで、より大きなファイルサイズに対応しています。

コミット

commit cdc556556c1d0213b3205dcd5b08655795fc215d
Author: Ian Lance Taylor <iant@golang.org>
Date:   Tue Jan 21 09:29:19 2014 -0800

    cmd/ld: support archives larger than 2G
    
    R=golang-codereviews, gobot, rsc
    CC=golang-codereviews
    https://golang.org/cl/53950043

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

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

元コミット内容

cmd/ld: support archives larger than 2G

変更の背景

この変更の背景には、Go言語のリンカ (cmd/ld) が、2GBを超えるサイズのアーカイブファイル(通常は静的ライブラリである .a ファイル)を処理できないという問題がありました。これは、ファイル内のオフセットを管理するために使用されていたデータ型が、32ビット符号付き整数 (int または int32) であったことに起因します。32ビット符号付き整数で表現できる最大値は約2GB(2^31 - 1バイト)であるため、これを超えるファイルサイズを正確に参照することができませんでした。

現代のソフトウェア開発では、特に大規模なプロジェクトや、多くの依存関係を持つアプリケーションにおいて、静的ライブラリのサイズが2GBを超えることは珍しくありません。このような状況でリンカが2GBの壁にぶつかると、ビルドプロセスが失敗し、開発者は大きなライブラリを分割するなどの回避策を講じる必要がありました。これは開発効率を著しく低下させる要因となります。

このコミットは、この根本的な問題を解決し、Goリンカがより大きなアーカイブファイルをシームレスに扱えるようにすることで、Go言語のツールチェインの堅牢性と実用性を向上させることを目的としています。

前提知識の解説

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

  • リンカ (Linker): リンカは、コンパイラによって生成されたオブジェクトファイル(機械語コードとデータを含むファイル)やライブラリファイルを結合し、実行可能なプログラムや共有ライブラリを生成するツールです。Go言語においては、cmd/ld がこの役割を担っています。
  • アーカイブファイル (Archive File / Static Library): 複数のオブジェクトファイルを一つにまとめたファイル形式です。Unix系システムでは通常 .a 拡張子を持ち、Windowsでは .lib 拡張子を持つことが多いです。プログラムがビルドされる際に、リンカはこれらのアーカイブファイルから必要なオブジェクトコードを抽出し、最終的な実行ファイルに組み込みます。
  • ファイルオフセット (File Offset): ファイルの先頭からの相対的な位置を示すバイト単位の数値です。ファイル内の特定の位置にあるデータにアクセスする際に使用されます。
  • データ型とサイズ制限:
    • int (C言語における): 通常、システムのアーキテクチャに依存する整数型です。多くの32ビットシステムでは32ビット(4バイト)の符号付き整数として扱われ、約 -2,147,483,648 から 2,147,483,647 までの値を表現できます。つまり、最大で約2GBまでのオフセットしか扱えません。
    • int32 (C言語における): 明示的に32ビットの符号付き整数を指します。int と同様に約2GBまでのオフセットしか扱えません。
    • vlong (Goリンカの内部型): このコミットで導入または使用されている型で、Goリンカの文脈では64ビットの符号付き整数を意味します。64ビット整数は、約 -9,223,372,036,854,775,808 から 9,223,372,036,854,775,807 までの値を表現でき、これは約9エクサバイト(EB)に相当します。これにより、事実上あらゆるサイズのファイルを扱うことが可能になります。
  • Biobuf: Goリンカの内部で使用されるバッファリングされたI/O構造体です。ファイルからの読み書きを効率化するために使用されます。
  • ar_hdr: アーカイブファイル内の各メンバー(オブジェクトファイル)のヘッダ情報を定義する構造体です。これには、メンバーの名前、サイズ、オフセットなどのメタデータが含まれます。

技術的詳細

このコミットの技術的な核心は、Goリンカがアーカイブファイル内のオフセットを扱う際に使用するデータ型を、32ビットから64ビットへと変更した点にあります。

具体的には、src/cmd/ld/lib.c ファイル内の nextar 関数と objfile 関数において、ファイルオフセットを表現する変数の型が int または int32 から vlong へと変更されています。

  • nextar 関数: この関数は、アーカイブファイル内で次のメンバー(オブジェクトファイル)を探す役割を担っています。以前は int off という引数で現在のオフセットを受け取っていましたが、これが vlong off に変更されました。これにより、2GBを超えるオフセットも正確に指定できるようになります。関数の戻り値も、次のメンバーのオフセットを示すために int から vlong に変更されています。
  • objfile 関数: この関数は、オブジェクトファイルまたはアーカイブファイルを処理する際に呼び出されます。この関数内でファイルオフセットを管理するローカル変数 offl の型が int32 から vlong に変更されました。これにより、リンカがアーカイブファイル内の各オブジェクトファイルの開始位置やサイズを正確に計算・参照できるようになります。

これらの変更は、リンカがアーカイブファイルを解析し、その内容を読み込む際の内部的なオフセット計算に直接影響を与えます。32ビットの制限が取り除かれることで、リンカはファイルサイズに起因するオーバーフローや不正なオフセット参照の問題を回避し、2GBを超えるアーカイブファイルも正しく処理できるようになります。

また、src/cmd/ld/lib.h ファイルでは、nextar 関数のプロトタイプ宣言が更新され、int 型の引数と戻り値が vlong 型に変更されています。これは、C言語におけるヘッダファイルと実装ファイル間での型の一貫性を保つために必要な変更です。

この変更は、Goリンカの低レベルなファイルI/O処理に直接関わるものであり、Go言語で大規模なアプリケーションをビルドする際の安定性と互換性を向上させる上で非常に重要です。

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

src/cmd/ld/lib.c

--- a/src/cmd/ld/lib.c
+++ b/src/cmd/ld/lib.c
@@ -269,8 +269,8 @@ loadlib(void)
  * look for the next file in an archive.
  * adapted from libmach.
  */
-int
-nextar(Biobuf *bp, int off, struct ar_hdr *a)
+static vlong
+nextar(Biobuf *bp, vlong off, struct ar_hdr *a)
  {
  	int r;
  	int32 arsize;
@@ -300,7 +300,7 @@ nextar(Biobuf *bp, int off, struct ar_hdr *a)
  void
  objfile(char *file, char *pkg)
  {
-	int32 off, l;
+	vlong off, l;
  	Biobuf *f;
  	char magbuf[SARMAG];
  	char pname[150];

src/cmd/ld/lib.h

--- a/src/cmd/ld/lib.h
+++ b/src/cmd/ld/lib.h
@@ -241,7 +241,6 @@ void*	mal(uint32 n);
  void	mark(LSym *s);
  void	mywhatsys(void);
  struct ar_hdr;
--int	nextar(Biobuf *bp, int off, struct ar_hdr *a);
+void	objfile(char *file, char *pkg); // This line seems to be a context line in the diff, not a direct change related to nextar.
  void	patch(void);
  int	pathchar(void);

注記: src/cmd/ld/lib.h のdiffにおいて、-int nextar(Biobuf *bp, int off, struct ar_hdr *a); の削除は正しいですが、+void objfile(char *file, char *pkg); の追加は、実際には nextar 関数のプロトタイプ宣言の変更を反映したものではなく、diffのコンテキスト表示の一部である可能性が高いです。本来の変更は nextar のプロトタイプが vlong を使用するように更新されることです。

コアとなるコードの解説

このコミットの主要な変更は、ファイルオフセットを扱う変数の型を int または int32 から vlong へと変更することです。

  1. src/cmd/ld/lib.cnextar 関数:

    • 変更前: int nextar(Biobuf *bp, int off, struct ar_hdr *a)
      • off 引数が int 型であり、2GBを超えるオフセットを表現できませんでした。
      • 関数の戻り値も int 型であり、次のアーカイブメンバーの開始オフセットが2GBを超えると正しく返せませんでした。
    • 変更後: static vlong nextar(Biobuf *bp, vlong off, struct ar_hdr *a)
      • off 引数が vlong 型(64ビット整数)に変更され、非常に大きなオフセットも正確に扱えるようになりました。
      • 関数の戻り値も vlong 型に変更され、2GBを超えるオフセットも正しく返せるようになりました。
      • static キーワードが追加されていますが、これは関数のスコープに関する変更であり、オフセットの型変更とは直接関係ありません。
  2. src/cmd/ld/lib.cobjfile 関数:

    • 変更前: int32 off, l;
      • offl はファイルオフセットやサイズを格納するローカル変数で、int32 型でした。これにより、2GBの制限がありました。
    • 変更後: vlong off, l;
      • offlvlong 型に変更され、2GBを超えるファイルオフセットやサイズも問題なく扱えるようになりました。
  3. src/cmd/ld/lib.hnextar 関数プロトタイプ宣言:

    • 変更前: int nextar(Biobuf *bp, int off, struct ar_hdr *a);
      • ヘッダファイルでの宣言も int 型でした。
    • 変更後: このコミットのdiffでは直接的な変更として表示されていませんが、lib.c の変更に合わせて、この宣言も vlong を使用するように更新される必要があります。これは、コンパイル時の型チェックの一貫性を保つために不可欠です。

これらの変更により、Goリンカはアーカイブファイル内の各オブジェクトファイルの物理的な位置を正確に特定し、2GBのファイルサイズ制限に縛られることなく、大規模な静的ライブラリをリンクできるようになりました。これは、Go言語で開発されるアプリケーションの規模が拡大するにつれて、そのビルドシステムの堅牢性を高める上で重要な改善です。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • C言語のデータ型に関する一般的な情報
  • アーカイブファイル形式 (ar) に関する一般的な情報
  • Go言語のリンカの内部構造に関する一般的な知識
  • GitHubのコミット履歴とdiffビューア
  • Gerritのコードレビューシステム