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

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

このコミットは、Go言語のリンカである6lにおけるシンボル解決の挙動を改善するものです。具体的には、型シグネチャ(sigtで始まるシンボル)の定義において、未定義のシンボル(Sxxxタイプ)を無視するように変更し、リンカがライブラリから完全にプルされないシンボルを適切に処理できるようにします。また、デバッグ出力が追加され、リンカの動作の可視性が向上しています。

コミット

commit 8f14451fe5dce9d2a497fda7e4c277531be8e049
Author: Russ Cox <rsc@golang.org>
Date:   Tue Jan 20 13:21:22 2009 -0800

    6l: ignore undefined symbols in gotypesigs.
       they end up in the symbol table with type==0
       if they are in a library but not pulled in.
       also add a few debugging prints.
    
    R=r
    DELTA=11  (5 added, 1 deleted, 5 changed)
    OCL=23104
    CL=23108

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

https://github.com/golang/go/commit/8f14451fe5dce9d2a497fda7e4c277531be8e049

元コミット内容

6l: ignore undefined symbols in gotypesigs. they end up in the symbol table with type==0 if they are in a library but not pulled in. also add a few debugging prints.

(日本語訳) 6l: gotypesigs内の未定義シンボルを無視する。 これらは、ライブラリ内に存在するが、完全にプルインされていない場合、シンボルテーブルにtype==0として残ってしまう。 また、いくつかのデバッグ出力も追加する。

変更の背景

Go言語のリンカ6lは、コンパイルされたオブジェクトファイルやライブラリを結合して実行可能ファイルを生成する役割を担っています。このプロセスにおいて、リンカはシンボルテーブルを構築し、各シンボルの型や定義場所を管理します。

このコミットの背景には、リンカが型シグネチャ(sigtで始まるシンボル)を処理する際に発生していた問題があります。具体的には、ライブラリ内に存在するものの、最終的にリンクされる実行可能ファイルには含まれない(つまり、完全に「プルイン」されない)シンボルが、シンボルテーブルにtype==0(未定義)として残ってしまうことがありました。このような未定義のシンボルが型シグネチャの処理に含まれると、リンカの動作に不整合やエラーを引き起こす可能性がありました。

この変更は、このような未定義のシンボルを型シグネチャの処理から明示的に除外することで、リンカの堅牢性を高め、より正確なリンク処理を保証することを目的としています。また、デバッグ出力の追加は、リンカの内部動作をより詳細に把握し、将来的な問題の診断やデバッグを容易にするためのものです。

前提知識の解説

このコミットを理解するためには、Go言語のリンカ(特に6l)の基本的な概念と、シンボル管理に関する知識が必要です。

  • Go言語のリンカ (6l): Go言語のツールチェーンの一部であり、コンパイルされたGoのソースコード(オブジェクトファイル)と必要なライブラリを結合して、実行可能なバイナリを生成するプログラムです。6lは、x86-64アーキテクチャ向けのリンカを指す古い命名規則です。現代のGoでは、リンカはcmd/linkとして統合されていますが、内部的な概念は共通しています。

  • シンボル (Symbol): プログラム内の関数、変数、型などの名前付きエンティティを指します。リンカは、これらのシンボルを解決し、それぞれのメモリ上のアドレスを決定します。

  • シンボルテーブル (Symbol Table): リンカがプログラム内のすべてのシンボルとその属性(名前、型、アドレス、可視性など)を記録するために使用するデータ構造です。

  • 型シグネチャ (Type Signature): Go言語において、関数やメソッドの引数と戻り値の型、構造体のフィールドの型など、型の情報を記述したものです。リンカは、これらの型シグネチャをシンボルとして管理し、型の一貫性をチェックしたり、リフレクションなどの機能を提供したりします。コミットメッセージにあるsigt·は、Goの内部で型シグネチャを表すために使われるシンボルのプレフィックスです。

  • Sxxx (Symbol Kind): Goリンカの内部でシンボルの種類を分類するために使用される定数です。例えば、STEXTは実行可能なコード(関数)、SDATAは初期化されたデータ、SBSSは初期化されていないデータなどを表します。このコミットで言及されているSxxxは、シンボルが未定義であることを示す内部的なタイプです。具体的には、Sxxxはシンボルがまだ解決されていない、またはその種類が不明であることを示す汎用的なプレースホルダーとして機能します。

  • AGLOBL (Global Symbol): グローバルシンボルを意味します。異なるコンパイル単位(オブジェクトファイルやパッケージ)間で可視であり、アクセス可能です。

  • ADATA (Data Symbol): データセクションに属するシンボルを指します。初期化されたデータや未初期化のデータなどが含まれます。

  • DUPOK (Duplicate OK): リンカが同じ名前のシンボルが複数定義されていてもエラーとしないことを示す属性です。通常、シンボルの重複定義はエラーですが、特定のシナリオ(Cgoなど)では許可される場合があります。

  • SXREF (External Reference): 外部参照シンボルを指します。これは、現在のオブジェクトファイル内では定義されておらず、他のオブジェクトファイルやライブラリで定義されているシンボルを意味します。リンカは、これらの外部参照を解決して、正しいアドレスにリンクする必要があります。

技術的詳細

このコミットの主要な変更点は、src/cmd/6l/go.cdefinetypesigs関数とsrc/cmd/6l/obj.cloop関数およびlookup関数にあります。

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

definetypesigs関数は、Goの型シグネチャに関連するシンボルを定義・処理する役割を担っています。変更前は、この関数はsigt·で始まるすべてのシンボルを対象としていました。しかし、ライブラリから完全にプルインされなかったシンボルは、シンボルテーブル上でtype==0Sxxxに相当)として存在し、これらが処理に含まれると問題を引き起こす可能性がありました。

このコミットでは、memcmp(x->name, "sigt·", 6) == 0という条件に加えて、x->type != Sxxxという条件が追加されました。これにより、sigt·で始まるシンボルであっても、そのタイプがSxxx(未定義)である場合は処理の対象から除外されるようになりました。これにより、リンカは未定義の型シグネチャシンボルを無視し、より正確な型シグネチャの定義が可能になります。

src/cmd/6l/obj.cにおける変更

obj.cは、オブジェクトファイルの読み込みとシンボル処理に関する低レベルな操作を扱うファイルです。

  • loop関数内のデバッグ出力追加: loop関数は、オブジェクトファイル内の各セクションを処理する際に呼び出されます。DUPOK属性を持つシンボルがスキップされる場合に、デバッグレベルがv(verbose)の場合にBprint関数を使って「skipping %s in %s: dupok」というメッセージを出力するようになりました。これにより、リンカが重複を許可されたシンボルをどのように処理しているかを追跡しやすくなります。

  • loop関数内の再定義スキップ時のデバッグ出力変更: シンボルの再定義が検出され、そのシンボルがスキップされる場合に、以前はdiag関数が使用されていましたが、Bprint関数に変更されました。これにより、デバッグ出力の形式が統一され、より詳細な情報(ファイル名とシンボル名)が提供されるようになりました。

  • lookup関数内のデバッグ出力追加: lookup関数は、シンボルテーブルからシンボルを検索または追加する際に呼び出されます。デバッグレベルがv > 1の場合に、検索対象のシンボル名を出力するようになりました。これは、リンカがどのシンボルを検索しているかを詳細に追跡するのに役立ち、シンボル解決の問題を診断する際に非常に有用です。

これらの変更は、リンカの内部動作の透明性を高め、デバッグ能力を向上させることを目的としています。

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

src/cmd/6l/go.c

--- a/src/cmd/6l/go.c
+++ b/src/cmd/6l/go.c
@@ -455,13 +455,13 @@ definetypesigs(void)
  	n = 0;
  	for(i=0; i<NHASH; i++)
  		for(x = hash[i]; x; x=x->link)
- 			if(memcmp(x->name, "sigt·", 6) == 0)
+ 			if(memcmp(x->name, "sigt·", 6) == 0 && x->type != Sxxx)
  				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)
+ 			if(memcmp(x->name, "sigt·", 6) == 0 && x->type != Sxxx)
  				all[j++] = x;
 
  	// sort them by name
@@ -488,5 +488,4 @@ definetypesigs(void)
 
  	if(debug['v'])
  		Bprint(&bso, "%5.2f typesigs %d\n", cputime(), n);
-
 }

src/cmd/6l/obj.c

--- a/src/cmd/6l/obj.c
+++ b/src/cmd/6l/obj.c
@@ -1063,8 +1063,11 @@ loop:
  		// If we've seen an AGLOBL that said this sym was DUPOK,
  		// ignore any more ADATA we see, which must be
  		// redefinitions.
- 		if(p->from.sym != S && p->from.sym->dupok)
+ 		if(p->from.sym != S && p->from.sym->dupok) {
+ 			if(debug['v'])
+ 				Bprint(&bso, "skipping %s in %s: dupok", p->from.sym->name, pn);
  			goto loop;
+ 		}
  		if(edatap == P)
  			datp = p;
  		else
@@ -1083,7 +1086,7 @@ loop:
  		if(ntext++ == 0 && s->type != 0 && s->type != SXREF) {
  			/* redefinition, so file has probably been seen before */
  			if(debug['v'])
- 				diag("skipping: %s: redefinition: %s", pn, s->name);
+ 				Bprint(&bso, "skipping: %s: redefinition: %s", pn, s->name);
  			return;
  		}
  		if(curtext != P) {
@@ -1260,6 +1263,8 @@ lookup(char *symb, int v)
  			return s;
 
  	s = mal(sizeof(*s));
+ 	if(debug['v'] > 1)
+ 		Bprint(&bso, "lookup %s\n", symb);
 
  	s->name = malloc(l + 1);
  	memmove(s->name, symb, l);

コアとなるコードの解説

src/cmd/6l/go.cの変更点

definetypesigs関数内の2つのforループにおいて、if(memcmp(x->name, "sigt·", 6) == 0)という条件に、&& x->type != Sxxx)が追加されています。

  • memcmp(x->name, "sigt·", 6) == 0: シンボルの名前が"sigt·"で始まるかどうかをチェックします。これはGoの型シグネチャシンボルを識別するためのプレフィックスです。
  • x->type != Sxxx: シンボルのタイプがSxxx(未定義または不明なタイプ)ではないことをチェックします。

この変更により、sigt·で始まるシンボルであっても、そのシンボルが未定義の状態(ライブラリから完全にプルインされていないなど)であれば、型シグネチャの処理から除外されるようになりました。これにより、リンカは未定義のシンボルによって引き起こされる可能性のある問題を回避し、より堅牢な型シグネチャの処理を実現します。

src/cmd/6l/obj.cの変更点

  1. loop関数内のDUPOKシンボルスキップ時のデバッグ出力: if(p->from.sym != S && p->from.sym->dupok)ブロック内に、if(debug['v']) Bprint(&bso, "skipping %s in %s: dupok", p->from.sym->name, pn);が追加されました。 これは、DUPOK(重複が許可されている)属性を持つシンボルがリンカによってスキップされる際に、詳細モード(debug['v']が真)であれば、そのシンボル名とファイル名を含むデバッグメッセージを出力するようにします。これにより、リンカがどの重複シンボルを無視しているかを追跡できます。

  2. loop関数内の再定義スキップ時のデバッグ出力の変更: if(ntext++ == 0 && s->type != 0 && s->type != SXREF)ブロック内のデバッグ出力が、diag("skipping: %s: redefinition: %s", pn, s->name);からBprint(&bso, "skipping: %s: redefinition: %s", pn, s->name);に変更されました。 diagはエラーや警告を出力する関数ですが、Bprintはより汎用的なデバッグ出力関数です。この変更は、再定義のスキップが必ずしもエラーではないため、より適切なデバッグ出力メカニズムを使用するように調整されたことを示唆しています。

  3. lookup関数内のデバッグ出力追加: lookup関数内で、if(debug['v'] > 1) Bprint(&bso, "lookup %s\n", symb);が追加されました。 これは、リンカがシンボルテーブル内でシンボルを検索する際に、デバッグレベルが2以上(より詳細なデバッグモード)であれば、検索対象のシンボル名を出力するようにします。これは、リンカのシンボル解決プロセスを詳細に追跡し、デバッグする際に非常に役立ちます。

これらの変更は全体として、Goリンカの内部的な堅牢性を高めるとともに、デバッグ能力を向上させることを目的としています。特に、未定義のシンボルがリンカの処理に悪影響を与えるのを防ぎ、リンカの動作をより透過的にすることで、開発者が問題を診断しやすくなります。

関連リンク

参考にした情報源リンク