[インデックス 17950] ファイルの概要
このコミットは、Go言語のリンカ(liblink
)とコンパイラ(cmd/gc
)がPlan 9オペレーティングシステム上で動作する際に発生していた、型シグネチャの非互換性の問題を修正するものです。具体的には、Node
、Section
、Array
といった構造体の定義が、異なるコンパイルユニット間で不整合を起こすことを防ぐために、#pragma incomplete
ディレクティブが追加されています。
コミット
commit 4321beba85d2317b86911401dd25bb87a48677e7
Author: David du Colombier <0intro@gmail.com>
Date: Tue Dec 10 08:42:41 2013 -0500
liblink, cmd/gc: fix incompatible type signatures on Plan 9
R=ality, golang-dev, r, rsc
CC=golang-dev
https://golang.org/cl/39640043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/4321beba85d2317b86911401dd25bb87a48677e7
元コミット内容
liblink, cmd/gc: fix incompatible type signatures on Plan 9
このコミットは、Go言語のリンカ(liblink
)とコンパイラ(cmd/gc
)において、Plan 9環境での型シグネチャの非互換性を修正します。
変更の背景
Go言語のツールチェインは、様々なオペレーティングシステムをサポートするように設計されています。その中には、ベル研究所で開発された分散オペレーティングシステムであるPlan 9も含まれます。Goのコンパイラ(cmd/gc
)やリンカ(liblink
)は、C言語で書かれた部分とGo言語で書かれた部分が混在しており、特にC言語で書かれた部分では、異なるコンパイルユニット(例えば、リンカとコンパイラ、あるいはリンカ内部の異なるモジュール)間で共通のデータ構造を共有する必要があります。
このコミットが修正しようとしている問題は、Plan 9環境において、これらの共通データ構造(Node
、Section
、Array
など)の型定義が、異なるコンパイルユニット間で「非互換な型シグネチャ」として認識されてしまうことでした。これは通常、ヘッダファイルのインクルード順序、マクロ定義、またはコンパイラの特定の挙動によって、同じ構造体名であっても異なる内部表現やアラインメントが適用されてしまう場合に発生します。結果として、リンカやコンパイラが正しく連携できず、ビルドエラーや実行時エラーを引き起こす可能性がありました。
特に、Goのツールチェインでは、8l
(Plan 9向けのリンカ)や8g
(Plan 9向けのGoコンパイラ)といった、ターゲットアーキテクチャ(この場合は8l/8gは386アーキテクチャを指すことが多い)に特化したツールが存在します。これらのツールが、liblink
やlibgc
(Goコンパイラのバックエンドライブラリ)と連携する際に、型定義の不一致が問題となっていたと考えられます。
前提知識の解説
- Plan 9: ベル研究所で開発された分散オペレーティングシステム。UNIXの思想をさらに推し進め、すべてのリソースをファイルとして扱うという特徴を持つ。Go言語の開発者の一部はPlan 9の開発にも携わっており、Go言語の設計思想にも影響を与えている。
- Go言語のツールチェイン: Go言語のプログラムをビルド、実行するために必要な一連のツール群。主要なものとして、
go
コマンド、コンパイラ(cmd/gc
)、リンカ(liblink
)、アセンブラ(cmd/goasm
)などがある。 cmd/gc
: Go言語の公式コンパイラ。Goのソースコードを機械語に変換する役割を担う。liblink
: Go言語のリンカのライブラリ部分。コンパイルされたオブジェクトファイルを結合し、実行可能ファイルを生成する。8l
/8g
: Plan 9の命名規則に由来するGoのツールチェインのコンポーネント名。8
はIntel 80386アーキテクチャを指し、l
はリンカ、g
はGoコンパイラを指す。つまり、8l
は386アーキテクチャ向けのリンカ、8g
は386アーキテクチャ向けのGoコンパイラを意味する。- 型シグネチャの非互換性: C言語において、同じ名前の構造体や関数であっても、異なるコンパイルユニットで定義された際に、その内部表現(メンバの順序、サイズ、アラインメントなど)が異なってしまうこと。これは、ヘッダファイルのインクルードミス、コンパイラオプションの違い、または特定のプラットフォームにおけるコンパイラの挙動によって発生しうる。結果として、異なるコンパイルユニット間でデータをやり取りする際に、メモリレイアウトの不一致によりデータ破損やクラッシュを引き起こす。
#pragma incomplete
: C言語のプリプロセッサディレクティブの一つ。これは、特定の構造体や共用体が「不完全型(incomplete type)」であることをコンパイラに伝えるために使用される。不完全型とは、そのサイズやメンバがまだ完全に定義されていない型のこと。通常、前方宣言(struct Node;
)を行うことで不完全型として扱われるが、#pragma incomplete
は、特にリンカや他のツールとの連携において、型定義の完全性を強制しないようにするために使用されることがある。これにより、異なるコンパイルユニットが同じ構造体を参照する際に、その完全な定義がなくてもコンパイルを進めることができ、リンカが最終的にすべての定義を解決することを期待する。この文脈では、異なるコンパイルユニットが同じ構造体を参照する際に、その完全な定義がなくてもコンパイルを進めることができ、リンカが最終的にすべての定義を解決することを期待する。
技術的詳細
このコミットの核心は、C言語のプリプロセッサディレクティブである#pragma incomplete
を使用して、特定の構造体(Node
、Section
、Array
)の型シグネチャの非互換性を回避することです。
通常、C言語では、構造体を使用する前にその完全な定義が必要です。しかし、相互参照する構造体や、異なるコンパイルユニット間で共有されるが、それぞれのコンパイルユニットではその完全な定義が必要ない(あるいは、完全な定義が別の場所にある)場合に、前方宣言(struct Node;
)が用いられます。これにより、コンパイラはその構造体が存在することを知り、ポインタ型として扱うことができます。
しかし、Plan 9環境における特定のコンパイラ(8l
や8g
)の挙動、またはGoツールチェインのビルドシステムにおける複雑な依存関係により、liblink
とcmd/gc
の間で、これらの構造体の「完全な」型定義が異なる形で解釈されてしまう問題が発生していました。これは、例えば、あるファイルでは構造体のメンバが完全に定義されているが、別のファイルではその構造体へのポインタのみが使用され、その際にコンパイラが異なる内部表現を生成してしまう、といった状況が考えられます。
#pragma incomplete struct Node
のようなディレクティブは、コンパイラに対して「Node
という構造体は存在するが、このコンパイルユニットではその完全な定義は期待しない(あるいは、不完全な型として扱う)」という指示を与えます。これにより、コンパイラは型チェックを緩和し、異なるコンパイルユニット間で型定義の厳密な一致を強制することなく、コンパイルを進めることができます。最終的な型解決はリンカの役割となります。
このアプローチは、特にクロスコンパイル環境や、異なるコンパイラバージョン、あるいは特定のOS環境(この場合はPlan 9)のコンパイラが、標準Cの挙動とは異なる微妙な解釈をする場合に有効な回避策となります。Goのツールチェインは、C言語で書かれた部分が多いため、このようなC言語のコンパイラ固有の問題に対処する必要がありました。
コアとなるコードの変更箇所
diff --git a/include/link.h b/include/link.h
index 1d6aec49ee..abaa6ad401 100644
--- a/include/link.h
+++ b/include/link.h
@@ -43,6 +43,9 @@ typedef struct Library Library;\n typedef struct Pcln Pcln;\n typedef struct Pcdata Pcdata;\n \n+// prevent incompatible type signatures between liblink and 8l on Plan 9\n+#pragma incomplete struct Node\n+\n struct Addr\n {\n \tvlong offset;\n@@ -111,6 +114,9 @@ struct Prog\n char mode; /* 16, 32, or 64 */\n };\n \n+// prevent incompatible type signatures between liblink and 8l on Plan 9\n+#pragma incomplete struct Section\n+\n struct LSym\n {\n char* name;\
diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h
index 6faf4c446c..cc9a5eeaf8 100644
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -129,6 +129,9 @@ struct Val\n } u;\n };\n \n+// prevent incompatible type signatures between libgc and 8g on Plan 9\n+#pragma incomplete struct Array\n+\n typedef struct Array Array;\n typedef struct Bvec Bvec;\n typedef struct Pkg Pkg;\
コアとなるコードの解説
このコミットでは、以下の3つのファイルに#pragma incomplete
ディレクティブが追加されています。
-
include/link.h
:typedef struct Library Library;
typedef struct Pcln Pcln;
typedef struct Pcdata Pcdata;
これらのtypedef
宣言の直後に、Node
構造体に対する#pragma incomplete
が追加されています。
// prevent incompatible type signatures between liblink and 8l on Plan 9 #pragma incomplete struct Node
これは、
liblink
(リンカ)と8l
(Plan 9向けのリンカ)の間でNode
構造体の型シグネチャが非互換になるのを防ぐためのものです。Node
はGoのAST(抽象構文木)のノードを表す重要な構造体であり、コンパイラとリンカの間で共有される可能性があります。さらに、
struct Prog
の定義の後に、Section
構造体に対する#pragma incomplete
が追加されています。// prevent incompatible type signatures between liblink and 8l on Plan 9 #pragma incomplete struct Section
Section
は、実行可能ファイルのセクション(コードセクション、データセクションなど)を表す構造体であり、リンカが主に扱うものです。ここでもliblink
と8l
間の非互換性を防ぐ目的があります。 -
src/cmd/gc/go.h
:struct Val
の定義の後に、Array
構造体に対する#pragma incomplete
が追加されています。
// prevent incompatible type signatures between libgc and 8g on Plan 9 #pragma incomplete struct Array
これは、
libgc
(Goコンパイラのバックエンドライブラリ)と8g
(Plan 9向けのGoコンパイラ)の間でArray
構造体の型シグネチャが非互換になるのを防ぐためのものです。Array
はGoの配列型を表す構造体であり、コンパイラ内部で頻繁に利用されます。
これらの変更は、GoツールチェインのC言語部分において、Plan 9環境特有のコンパイラの挙動やビルドシステムの問題に起因する型定義の不整合を、#pragma incomplete
というプリプロセッサディレクティブを用いて回避することを目的としています。これにより、異なるコンパイルユニットが同じ構造体を参照する際に、その完全な定義がなくてもコンパイルを進めることができ、リンカが最終的にすべての定義を解決することを期待します。
関連リンク
- Go CL 39640043: https://golang.org/cl/39640043
参考にした情報源リンク
- Plan 9 from Bell Labs: https://9p.io/plan9/
- Go Programming Language: https://go.dev/
- C言語の
#pragma
ディレクティブに関する一般的な情報 (例: GCC Pragma Directives): https://gcc.gnu.org/onlinedocs/gcc/Pragmas.html - C言語の不完全型に関する情報 (例: C Incomplete Types): https://en.cppreference.com/w/c/language/type#Incomplete_types
- Goのツールチェインの構造に関する一般的な情報 (例: Go Compiler Internals): https://go.dev/doc/articles/go_compiler_internals.html
- GoのツールチェインにおけるPlan 9のサポートに関する情報 (例: Go on Plan 9): https://go.dev/doc/install/plan9
- Goのソースコード(
include/link.h
,src/cmd/gc/go.h
)