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

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

このコミットは、Go言語のリンカ 6l における型情報(特に型シグネチャ gotypesigs)の生成方法の改善と、それを利用した新しいランタイム関数 sys.unreflect の追加に関するものです。これにより、Goのランタイムがより柔軟に型情報を扱い、リフレクション機能が拡張されました。

コミット

commit 47caf6424c9bcaedb100feae83032b5afcb1bcc0
Author: Russ Cox <rsc@golang.org>
Date:   Mon Nov 3 16:03:12 2008 -0800

    6l: generate gotypesigs on demand.
    add sys.unreflect, which uses gotypesigs.

    R=r
    DELTA=170  (152 added, 12 deleted, 6 changed)
    OCL=18396
    CL=18404

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

https://github.com/golang/go/commit/47caf6424c9bcaedb100feae83032b5afcb1bcc0

元コミット内容

6l: generate gotypesigs on demand.
add sys.unreflect, which uses gotypesigs.

R=r
DELTA=170  (152 added, 12 deleted, 6 changed)
OCL=18396
CL=18404

変更の背景

このコミットが行われた2008年11月は、Go言語がまだ一般に公開される前の初期開発段階でした。Go言語は静的型付け言語でありながら、動的な型情報へのアクセス(リフレクション)を可能にすることを目指していました。初期のGo言語では、型情報はコンパイル時に生成され、ランタイムで利用できるようにリンカによって処理される必要がありました。

このコミットの主な背景は以下の2点と考えられます。

  1. 型情報の効率的な管理: gotypesigs (Go Type Signatures) は、Goの型システムにおける重要な要素であり、インターフェースの実装チェックやリフレクション操作に利用されます。初期の実装では、これらの型情報が常に生成されていた可能性がありますが、「on demand (オンデマンド)」での生成に切り替えることで、リンカの処理効率や生成されるバイナリのサイズを最適化しようとしたと考えられます。これは、必要な時に必要な型情報だけを生成することで、無駄を省くアプローチです。
  2. リフレクション機能の拡張: Go言語のリフレクションは、実行時に型情報を検査し、操作する強力な機能です。sys.reflect のような関数は、Goの値をその型情報(uint64string で表現される)に変換するために存在していました。しかし、その逆の操作、つまり型情報と生のデータからGoのインターフェース値を再構築する機能 (unreflect) が求められていたと考えられます。これは、例えばシリアライズされたデータや外部からの入力に基づいて、動的にGoの型を再構築するような高度なユースケースに対応するために必要でした。

この変更は、Go言語のランタイムにおける型システムの柔軟性と効率性を向上させ、将来のリフレクション機能の基盤を強化することを目的としています。

前提知識の解説

このコミットを理解するためには、以下のGo言語およびコンパイラ/リンカに関する基本的な知識が必要です。

  1. Go言語のコンパイルとリンク:

    • Goのソースコード (.go ファイル) は、まずコンパイラ (gc など) によってオブジェクトファイル (.6 ファイルなど、アーキテクチャによって異なる) にコンパイルされます。
    • これらのオブジェクトファイルは、リンカ (6l など) によって結合され、実行可能なバイナリが生成されます。リンカは、シンボルの解決、コードの配置、ランタイムに必要なデータの準備などを行います。
    • 6l は、当時のGo言語のx86-64アーキテクチャ向けリンカの名称です。
  2. Go言語のリフレクション:

    • Goのリフレクションは、reflect パッケージを通じて提供される機能で、プログラムの実行中に変数や関数の型情報を検査したり、値を動的に操作したりすることを可能にします。
    • Goのインターフェースは、リフレクションの基盤となる重要な概念です。インターフェース値は、内部的に「型情報」と「値データ」のペアとして表現されます。
    • sys.reflect (当時の内部関数名) は、Goのインターフェース値からその型情報と値データを抽出するような機能を提供していたと考えられます。
  3. 型シグネチャ (Type Signatures):

    • Goのランタイムは、プログラム内で使用されるすべての型に関する情報を保持しています。これには、型の名前、構造、メソッドセットなどが含まれます。
    • gotypesigs は、これらの型情報をリンカが生成し、ランタイムがアクセスできるようにするためのデータ構造またはセクションを指します。これは、インターフェースの動的な型チェックやリフレクション操作の際に参照されます。
    • Sigt は "Signature Type" の略で、個々の型シグネチャを表す構造体であると推測されます。
  4. リンカにおけるデータセクション:

    • リンカは、コンパイルされたコードだけでなく、プログラムが実行時に必要とする静的なデータもバイナリに埋め込みます。これには、文字列定数、グローバル変数、そして型情報のようなランタイムデータが含まれます。
    • gotypestrings は、Goの型名を文字列として格納するセクションであると推測されます。
    • gotypesigs は、より詳細な型構造に関する情報を格納するセクションであると考えられます。
  5. Prog 構造体と newdata 関数:

    • Prog は、リンカが扱う命令やデータのエントリを表す内部構造体です。
    • newdata 関数は、リンカが新しいデータエントリを生成し、それを特定のシンボルに関連付けるために使用されるユーティリティ関数です。

技術的詳細

このコミットの技術的な詳細は、主にGoリンカ 6l とランタイム src/runtime/iface.c における型情報の管理とリフレクション機能の拡張に焦点を当てています。

1. gotypesigs のオンデマンド生成

  • src/cmd/6l/go.c の変更:
    • definetypesigs という新しい関数が追加されました。この関数は、リンカが gotypesigs というシンボルへの未定義参照を検出した場合に呼び出されます。これは、「オンデマンド」生成のメカニズムを示しています。つまり、プログラムが gotypesigs を必要としない限り、このデータは生成されません。
    • definetypesigs は、リンカが認識しているすべての型シグネチャ (sigt· プレフィックスを持つシンボル) を収集し、それらを名前でソートします。
    • ソートされた型シグネチャへのポインタの配列 (gotypesigs) が、リンカによってデータセクションに書き込まれます。各ポインタは PtrSize (8バイト、x86-64の場合) のサイズを持ちます。
    • 型シグネチャの総数 (ngotypesigs) も int32 型の変数としてデータセクションに書き込まれます。
    • 既存の definetypestrings 関数も、newdata 関数の呼び出し方法が変更されています。これは、リンカのデータ生成メカニズムの統一化を示唆しています。
  • src/cmd/6l/obj.c の変更:
    • リンカのメイン関数 main で、definetypestrings() の後に definetypesigs() が呼び出されるようになりました。これにより、リンカの処理フローに型シグネチャの生成が組み込まれます。

2. sys.unreflect の追加

  • src/cmd/gc/sys.go の変更:
    • export func unreflect(uint64, string) (ret interface { }); という新しい関数が追加されました。これは、Goのソースコードから sys.unreflect を呼び出すための宣言です。uint64 は値データ、string は型名を表すと推測されます。
  • src/cmd/gc/sysimport.c の変更:
    • コンパイラの内部で sys.unreflect が認識されるように、そのシグネチャが sysimport 文字列に追加されました。
  • src/runtime/iface.c の変更:
    • sys·unreflect というC言語の関数が実装されました。これが sys.unreflect の実際のランタイム実装です。
    • この関数は、uint64 の値データと string の型名を受け取ります。
    • findtype ヘルパー関数が導入されました。この関数は、与えられた型名に対応する Sigt (型シグネチャ) を gotypesigs 配列から検索します。
    • cmpstringchars は、Goの文字列とCスタイルの文字列を比較するためのヘルパー関数です。
    • fakesigt ヘルパー関数は、gotypesigs に見つからない型名に対して、一時的な Sigt を生成する役割を担っています。これは、未知の型を扱う際のエラーハンドリングや、動的な型生成の初期段階で利用される可能性があります。ただし、コメントに TODO(rsc): What to do here? とあることから、この部分の設計はまだ初期段階であったことが伺えます。
    • sys·unreflect は、findtype で取得した Sigt と入力された値データ (it) を使用して、新しいインターフェース値 (retim, retit) を構築します。hashmap(sigi·inter, findtype(type), 0) の部分は、型シグネチャに基づいてインターフェースの内部表現を構築していることを示唆しています。

3. newdata 関数の変更

  • src/cmd/6l/pass.c の変更:
    • newdata 関数の宣言が static Prog* から Prog* に変更されました。これは、この関数が他のファイルからもアクセス可能になったことを意味し、リンカのデータ生成ロジックがよりモジュール化されたことを示唆しています。

これらの変更は、Go言語のランタイムが、コンパイル時に生成された型情報をより効率的に利用し、動的な型操作(リフレクション)の能力を向上させるための重要なステップでした。特に sys.unreflect の導入は、Goのインターフェースが単なる抽象化のツールではなく、実行時の型変換と操作のための強力なメカニズムであることを示しています。

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

このコミットのコアとなるコードの変更は、主に以下のファイルに集中しています。

  1. src/cmd/6l/go.c:

    • definetypesigs 関数の追加 (約50行の新規コード)。
    • definetypestrings 関数内の newdata 呼び出しの変更。
    • symcmp 関数の追加。
  2. src/runtime/iface.c:

    • sys·unreflect 関数の追加 (約40行の新規コード)。
    • fakesigt 関数の追加。
    • cmpstringchars 関数の追加。
    • findtype 関数の追加。
    • gotypesigsngotypesigs の外部宣言。
  3. src/cmd/gc/sys.go:

    • export func unreflect(uint64, string) (ret interface { }); の追加。
  4. src/cmd/gc/sysimport.c:

    • "export func sys.unreflect (? uint64, ? string) (ret interface { })\\n" の追加。

コアとなるコードの解説

src/cmd/6l/go.c における definetypesigs

void
definetypesigs(void)
{
	int i, j, n;
	Sym **all, *s, *x;
	Prog *prog;

	if(debug['g'])
		return;

	if(debug['v'])
		Bprint(&bso, "%5.2f definetypesigs\\n", cputime());

	s = lookup("gotypesigs", 0);
	if(s->type == 0) // gotypesigsシンボルが参照されていない場合は何もしない
		return;
	if(s->type != SXREF) { // 既に定義されている場合はエラー
		diag("gotypesigs already defined");
		return;
	}
	s->type = SDATA; // シンボルをデータとしてマーク

	// すべてのsigt·プレフィックスを持つシンボルをリストアップ
	n = 0;
	for(i=0; i<NHASH; i++)
		for(x = hash[i]; x; x=x->link)
			if(memcmp(x->name, "sigt·", 6) == 0)
				n++;
	all = mal(n*sizeof all[0]);
	j = 0;
	for(i=0; i<NHASH; i++)
		for(x = hash[i]; x; x=x->link)
			if(memcmp(x->name, "sigt·", 6) == 0)
				all[j++] = x;

	// 名前でソート
	qsort(all, n, sizeof all[0], symcmp);

	// ポインタの配列としてデータセクションに出力
	enum { PtrSize = 8 }; // x86-64の場合
	for(i=0; i<n; i++) {
		prog = newdata(s, PtrSize*i, PtrSize, D_EXTERN);
		prog->to.type = D_ADDR;
		prog->to.index = D_EXTERN;
		prog->to.sym = all[i]; // 各sigt·シンボルへのアドレス
	}
	s->value = PtrSize*n; // gotypesigsのサイズ

	// ngotypesigs (型シグネチャの数) を出力
	s = lookup("ngotypesigs", 0);
	s->type = SDATA;
	s->value = sizeof(int32);
	prog = newdata(s, 0, sizeof(int32), D_EXTERN);
	prog->to.offset = n; // 型シグネチャの総数

	if(debug['v'])
		Bprint(&bso, "%5.2f typestrings %d\\n", cputime(), n);
}

この関数は、リンカが gotypesigs シンボルへの参照を検出した場合に、プログラムで使用されるすべての型シグネチャ (sigt· で始まるシンボル) を収集し、それらをソートして、実行時にアクセス可能なデータ構造としてバイナリに埋め込みます。これにより、ランタイムは動的に型情報を参照できるようになります。

src/runtime/iface.c における sys·unreflect

extern Sigt *gotypesigs[]; // リンカが生成した型シグネチャの配列
extern int32 ngotypesigs; // 型シグネチャの数

// ... (fakesigt, cmpstringchars, findtype の実装) ...

void
sys·unreflect(uint64 it, string type, Map *retim, void *retit)
{
	if(cmpstring(type, emptystring) == 0) { // 型名が空の場合はnilインターフェースを返す
		retim = 0;
		retit = 0;
	} else {
		// 型名に対応するSigt (型シグネチャ) を検索
		retim = hashmap(sigi·inter, findtype(type), 0);
		retit = (void*)it; // 値データを設定
	}
	FLUSH(&retim); // レジスタの値をメモリにフラッシュ
	FLUSH(&retit);
}

sys·unreflect は、Goのランタイムが提供する内部関数で、sys.unreflect Go関数から呼び出されます。この関数は、生のデータ (ituint64 として渡される) と型名 (typestring として渡される) を受け取り、それらからGoのインターフェース値を再構築します。findtype 関数を使って型名に対応する Sigtgotypesigs 配列から探し、その型情報と値データを組み合わせて新しいインターフェース値を生成します。これにより、Goのリフレクション機能が、型情報とデータからインターフェースを「再構築」する能力を獲得しました。

関連リンク

参考にした情報源リンク

  • Go言語の初期開発に関する情報 (Goの歴史): https://go.dev/doc/history
  • Go言語のコンパイラとリンカの内部構造に関する一般的な情報 (Goのツールチェーン): https://go.dev/doc/articles/go_command.html (これは現代のドキュメントですが、当時の概念を理解するのに役立ちます)
  • Go言語のインターフェースの内部表現に関する議論 (Stack Overflow, ブログ記事など): "Go interface internal representation" で検索すると多くの情報が見つかります。
  • Go言語のリンカ 6l に関する情報 (初期のGoのドキュメントやソースコードコメント): "Go 6l linker" で検索すると、当時のリンカの役割に関する情報が見つかることがあります。
  • Go言語の型システムに関する議論: "Go type system" で検索すると、型シグネチャの概念を理解するのに役立つ情報が見つかります。

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

このコミットは、Go言語のリンカ 6l における型情報(特に型シグネチャ gotypesigs)の生成方法の改善と、それを利用した新しいランタイム関数 sys.unreflect の追加に関するものです。これにより、Goのランタイムがより柔軟に型情報を扱い、リフレクション機能が拡張されました。

コミット

commit 47caf6424c9bcaedb100feae83032b5afcb1bcc0
Author: Russ Cox <rsc@golang.org>
Date:   Mon Nov 3 16:03:12 2008 -0800

    6l: generate gotypesigs on demand.
    add sys.unreflect, which uses gotypesigs.

    R=r
    DELTA=170  (152 added, 12 deleted, 6 changed)
    OCL=18396
    CL=18404

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

https://github.com/golang/go/commit/47caf6424c9bcaedb100feae83032b5afcb1bcc0

元コミット内容

6l: generate gotypesigs on demand.
add sys.unreflect, which uses gotypesigs.

R=r
DELTA=170  (152 added, 12 deleted, 6 changed)
OCL=18396
CL=18404

変更の背景

このコミットが行われた2008年11月は、Go言語がまだ一般に公開される前の初期開発段階でした。Go言語は静的型付け言語でありながら、動的な型情報へのアクセス(リフレクション)を可能にすることを目指していました。初期のGo言語では、型情報はコンパイル時に生成され、ランタイムで利用できるようにリンカによって処理される必要がありました。

このコミットの主な背景は以下の2点と考えられます。

  1. 型情報の効率的な管理: gotypesigs (Go Type Signatures) は、Goの型システムにおける重要な要素であり、インターフェースの実装チェックやリフレクション操作に利用されます。初期の実装では、これらの型情報が常に生成されていた可能性がありますが、「on demand (オンデマンド)」での生成に切り替えることで、リンカの処理効率や生成されるバイナリのサイズを最適化しようとしたと考えられます。これは、必要な時に必要な型情報だけを生成することで、無駄を省くアプローチです。
  2. リフレクション機能の拡張: Go言語のリフレクションは、実行時に型情報を検査し、操作する強力な機能です。sys.reflect のような関数は、Goの値をその型情報(uint64string で表現される)に変換するために存在していました。しかし、その逆の操作、つまり型情報と生のデータからGoのインターフェース値を再構築する機能 (unreflect) が求められていたと考えられます。これは、例えばシリアライズされたデータや外部からの入力に基づいて、動的にGoの型を再構築するような高度なユースケースに対応するために必要でした。

この変更は、Go言語のランタイムにおける型システムの柔軟性と効率性を向上させ、将来のリフレクション機能の基盤を強化することを目的としています。

前提知識の解説

このコミットを理解するためには、以下のGo言語およびコンパイラ/リンカに関する基本的な知識が必要です。

  1. Go言語のコンパイルとリンク:

    • Goのソースコード (.go ファイル) は、まずコンパイラ (gc など) によってオブジェクトファイル (.6 ファイルなど、アーキテクチャによって異なる) にコンパイルされます。
    • これらのオブジェクトファイルは、リンカ (6l など) によって結合され、実行可能なバイナリが生成されます。リンカは、シンボルの解決、コードの配置、ランタイムに必要なデータの準備などを行います。
    • 6l は、当時のGo言語のx86-64アーキテクチャ向けリンカの名称です。Go 1.5以降、6l のようなアーキテクチャ固有のリンカは go tool link に統合され、GOARCH 環境変数によってターゲットアーキテクチャが決定されるようになりました。しかし、このコミット当時は 6l が直接使用されていました。
  2. Go言語のリフレクション:

    • Goのリフレクションは、reflect パッケージを通じて提供される機能で、プログラムの実行中に変数や関数の型情報を検査したり、値を動的に操作したりすることを可能にします。
    • Goのインターフェースは、リフレクションの基盤となる重要な概念です。インターフェース値は、内部的に「型情報」と「値データ」のペアとして表現されます。
    • sys.reflect (当時の内部関数名) は、Goのインターフェース値からその型情報と値データを抽出するような機能を提供していたと考えられます。
  3. 型シグネチャ (Type Signatures):

    • Goのランタイムは、プログラム内で使用されるすべての型に関する情報を保持しています。これには、型の名前、構造、メソッドセットなどが含まれます。
    • gotypesigs は、これらの型情報をリンカが生成し、ランタイムがアクセスできるようにするためのデータ構造またはセクションを指します。これは、インターフェースの動的な型チェックやリフレクション操作の際に参照されます。
    • Sigt は "Signature Type" の略で、個々の型シグネチャを表す構造体であると推測されます。
  4. リンカにおけるデータセクション:

    • リンカは、コンパイルされたコードだけでなく、プログラムが実行時に必要とする静的なデータもバイナリに埋め込みます。これには、文字列定数、グローバル変数、そして型情報のようなランタイムデータが含まれます。
    • gotypestrings は、Goの型名を文字列として格納するセクションであると推測されます。
    • gotypesigs は、より詳細な型構造に関する情報を格納するセクションであると考えられます。
  5. Prog 構造体と newdata 関数:

    • Prog は、リンカが扱う命令やデータのエントリを表す内部構造体です。
    • newdata 関数は、リンカが新しいデータエントリを生成し、それを特定のシンボルに関連付けるために使用されるユーティリティ関数です。

技術的詳細

このコミットの技術的な詳細は、主にGoリンカ 6l とランタイム src/runtime/iface.c における型情報の管理とリフレクション機能の拡張に焦点を当てています。

1. gotypesigs のオンデマンド生成

  • src/cmd/6l/go.c の変更:
    • definetypesigs という新しい関数が追加されました。この関数は、リンカが gotypesigs というシンボルへの未定義参照を検出した場合に呼び出されます。これは、「オンデマンド」生成のメカニズムを示しています。つまり、プログラムが gotypesigs を必要としない限り、このデータは生成されません。
    • definetypesigs は、リンカが認識しているすべての型シグネチャ (sigt· プレフィックスを持つシンボル) を収集し、それらを名前でソートします。
    • ソートされた型シグネチャへのポインタの配列 (gotypesigs) が、リンカによってデータセクションに書き込まれます。各ポインタは PtrSize (8バイト、x86-64の場合) のサイズを持ちます。
    • 型シグネチャの総数 (ngotypesigs) も int32 型の変数としてデータセクションに書き込まれます。
    • 既存の definetypestrings 関数も、newdata 関数の呼び出し方法が変更されています。これは、リンカのデータ生成メカニズムの統一化を示唆しています。
  • src/cmd/6l/obj.c の変更:
    • リンカのメイン関数 main で、definetypestrings() の後に definetypesigs() が呼び出されるようになりました。これにより、リンカの処理フローに型シグネチャの生成が組み込まれます。

2. sys.unreflect の追加

  • src/cmd/gc/sys.go の変更:
    • export func unreflect(uint64, string) (ret interface { }); という新しい関数が追加されました。これは、Goのソースコードから sys.unreflect を呼び出すための宣言です。uint64 は値データ、string は型名を表すと推測されます。
  • src/cmd/gc/sysimport.c の変更:
    • コンパイラの内部で sys.unreflect が認識されるように、そのシグネチャが sysimport 文字列に追加されました。
  • src/runtime/iface.c の変更:
    • sys·unreflect というC言語の関数が実装されました。これが sys.unreflect の実際のランタイム実装です。
    • この関数は、uint64 の値データと string の型名を受け取ります。
    • findtype ヘルパー関数が導入されました。この関数は、与えられた型名に対応する Sigt (型シグネチャ) を gotypesigs 配列から検索します。
    • cmpstringchars は、Goの文字列とCスタイルの文字列を比較するためのヘルパー関数です。
    • fakesigt ヘルパー関数は、gotypesigs に見つからない型名に対して、一時的な Sigt を生成する役割を担っています。これは、未知の型を扱う際のエラーハンドリングや、動的な型生成の初期段階で利用される可能性があります。ただし、コメントに TODO(rsc): What to do here? とあることから、この部分の設計はまだ初期段階であったことが伺えます。
    • sys·unreflect は、findtype で取得した Sigt と入力された値データ (it) を使用して、新しいインターフェース値 (retim, retit) を構築します。hashmap(sigi·inter, findtype(type), 0) の部分は、型シグネチャに基づいてインターフェースの内部表現を構築していることを示唆しています。

3. newdata 関数の変更

  • src/cmd/6l/pass.c の変更:
    • newdata 関数の宣言が static Prog* から Prog* に変更されました。これは、この関数が他のファイルからもアクセス可能になったことを意味し、リンカのデータ生成ロジックがよりモジュール化されたことを示唆しています。

これらの変更は、Go言語のランタイムが、コンパイル時に生成された型情報をより効率的に利用し、動的な型操作(リフレクション)の能力を向上させるための重要なステップでした。特に sys.unreflect の導入は、Goのインターフェースが単なる抽象化のツールではなく、実行時の型変換と操作のための強力なメカニズムであることを示しています。

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

このコミットのコアとなるコードの変更は、主に以下のファイルに集中しています。

  1. src/cmd/6l/go.c:

    • definetypesigs 関数の追加 (約50行の新規コード)。
    • definetypestrings 関数内の newdata 呼び出しの変更。
    • symcmp 関数の追加。
  2. src/runtime/iface.c:

    • sys·unreflect 関数の追加 (約40行の新規コード)。
    • fakesigt 関数の追加。
    • cmpstringchars 関数の追加。
    • findtype 関数の追加。
    • gotypesigsngotypesigs の外部宣言。
  3. src/cmd/gc/sys.go:

    • export func unreflect(uint64, string) (ret interface { }); の追加。
  4. src/cmd/gc/sysimport.c:

    • "export func sys.unreflect (? uint64, ? string) (ret interface { })\\n" の追加。

コアとなるコードの解説

src/cmd/6l/go.c における definetypesigs

void
definetypesigs(void)
{
	int i, j, n;
	Sym **all, *s, *x;
	Prog *prog;

	if(debug['g'])
		return;

	if(debug['v'])
		Bprint(&bso, "%5.2f definetypesigs\\n", cputime());

	s = lookup("gotypesigs", 0);
	if(s->type == 0) // gotypesigsシンボルが参照されていない場合は何もしない
		return;
	if(s->type != SXREF) { // 既に定義されている場合はエラー
		diag("gotypesigs already defined");
		return;
	}
	s->type = SDATA; // シンボルをデータとしてマーク

	// すべてのsigt·プレフィックスを持つシンボルをリストアップ
	n = 0;
	for(i=0; i<NHASH; i++)
		for(x = hash[i]; x; x=x->link)
			if(memcmp(x->name, "sigt·", 6) == 0)
				n++;
	all = mal(n*sizeof all[0]);
	j = 0;
	for(i=0; i<NHASH; i++)
		for(x = hash[i]; x; x=x->link)
			if(memcmp(x->name, "sigt·", 6) == 0)
				all[j++] = x;

	// 名前でソート
	qsort(all, n, sizeof all[0], symcmp);

	// ポインタの配列としてデータセクションに出力
	enum { PtrSize = 8 }; // x86-64の場合
	for(i=0; i<n; i++) {
		prog = newdata(s, PtrSize*i, PtrSize, D_EXTERN);
		prog->to.type = D_ADDR;
		prog->to.index = D_EXTERN;
		prog->to.sym = all[i]; // 各sigt·シンボルへのアドレス
	}
	s->value = PtrSize*n; // gotypesigsのサイズ

	// ngotypesigs (型シグネチャの数) を出力
	s = lookup("ngotypesigs", 0);
	s->type = SDATA;
	s->value = sizeof(int32);
	prog = newdata(s, 0, sizeof(int32), D_EXTERN);
	prog->to.offset = n; // 型シグネチャの総数

	if(debug['v'])
		Bprint(&bso, "%5.2f typestrings %d\\n", cputime(), n);
}

この関数は、リンカが gotypesigs シンボルへの参照を検出した場合に、プログラムで使用されるすべての型シグネチャ (sigt· で始まるシンボル) を収集し、それらをソートして、実行時にアクセス可能なデータ構造としてバイナリに埋め込みます。これにより、ランタイムは動的に型情報を参照できるようになります。

src/runtime/iface.c における sys·unreflect

extern Sigt *gotypesigs[]; // リンカが生成した型シグネチャの配列
extern int32 ngotypesigs; // 型シグネチャの数

// ... (fakesigt, cmpstringchars, findtype の実装) ...

void
sys·unreflect(uint64 it, string type, Map *retim, void *retit)
{
	if(cmpstring(type, emptystring) == 0) { // 型名が空の場合はnilインターフェースを返す
		retim = 0;
		retit = 0;
	} else {
		// 型名に対応するSigt (型シグネチャ) を検索
		retim = hashmap(sigi·inter, findtype(type), 0);
		retit = (void*)it; // 値データを設定
	}
	FLUSH(&retim); // レジスタの値をメモリにフラッシュ
	FLUSH(&retit);
}

sys·unreflect は、Goのランタイムが提供する内部関数で、sys.unreflect Go関数から呼び出されます。この関数は、生のデータ (ituint64 として渡される) と型名 (typestring として渡される) を受け取り、それらからGoのインターフェース値を再構築します。findtype 関数を使って型名に対応する Sigtgotypesigs 配列から探し、その型情報と値データを組み合わせて新しいインターフェース値を生成します。これにより、Goのリフレクション機能が、型情報とデータからインターフェースを「再構築」する能力を獲得しました。

関連リンク

参考にした情報源リンク

  • Go言語の初期開発に関する情報 (Goの歴史): https://go.dev/doc/history
  • Go言語のコンパイラとリンカの内部構造に関する一般的な情報 (Goのツールチェーン): https://go.dev/doc/articles/go_command.html (これは現代のドキュメントですが、当時の概念を理解するのに役立ちます)
  • Go言語のインターフェースの内部表現に関する議論 (Stack Overflow, ブログ記事など): "Go interface internal representation" で検索すると多くの情報が見つかります。
  • Go言語のリンカ 6l に関する情報 (初期のGoのドキュメントやソースコードコメント): "Go 6l linker" で検索すると、当時のリンカの役割に関する情報が見つかることがあります。
  • Go言語の型システムに関する議論: "Go type system" で検索すると、型シグネチャの概念を理解するのに役立つ情報が見つかります。