[インデックス 16439] ファイルの概要
このコミットは、Go言語のツールチェインにおけるリンカ(5l, 6l, 8l)のソースコードに対して行われた修正です。具体的には、Plan 9環境でのビルドにおいてNULLが認識されない問題を解決するため、NULLをnilに置き換える変更が適用されています。この修正は、Goの初期開発におけるPlan 9との深い関連性、および異なるOS環境でのビルド互換性の重要性を示しています。
コミット
commit 0b88587d229d737a27f2dd0f8f75f5df41a11cf7
Author: Lucio De Re <lucio.dere@gmail.com>
Date: Thu May 30 15:02:10 2013 +1000
cmd/[568]l/obj.c: NULL is not recognised in Plan 9 build, use nil instead.
Fixes #5591.
R=golang-dev, dave, minux.ma, cshapiro
CC=carl shapiro <cshapiro, golang-dev
https://golang.org/cl/9839046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/0b88587d229d737a27f2dd0f8f75f5df41a11cf7
元コミット内容
このコミットは、Go言語のリンカ(src/cmd/5l/obj.c, src/cmd/6l/obj.c, src/cmd/8l/obj.c)において、ポインタマップデータが定義されていない場合にエラーを診断するコード行の修正です。元のコードでは、ポインタがNULLであるかどうかをチェックしていましたが、Plan 9環境でのビルドにおいてNULLが正しく認識されないという問題がありました。
具体的には、以下の行が変更されました。
--- a/src/cmd/5l/obj.c
+++ b/src/cmd/5l/obj.c
@@ -647,7 +647,7 @@ loop:
case APTRS:
if(skip)
goto casedef;
- if(cursym->nptrs == -1 || cursym->ptrs == NULL) {
+ if(cursym->nptrs == -1 || cursym->ptrs == nil) {
diag("ldobj1: pointer map data provided for %s without a definition", cursym->name);
errorexit();
}
同様の変更がsrc/cmd/6l/obj.cとsrc/cmd/8l/obj.cにも適用されています。
変更の背景
この変更の背景には、Go言語のツールチェインがPlan 9オペレーティングシステムの影響を強く受けているという歴史的経緯があります。Go言語の初期のコンパイラやリンカは、Plan 9のツールチェインをベースに開発されました。Plan 9のCコンパイラ(8c, 6c, 5cなど)やリンカ(8l, 6l, 5lなど)は、標準的なC言語の慣習とは異なる独自のキーワードやマクロを使用することがありました。
このコミットで修正された問題は、Plan 9環境でGoのツールチェインをビルドする際に、C言語の標準的なNULLマクロが正しく定義または認識されないために発生しました。結果として、cursym->ptrs == NULLという比較がコンパイルエラーを引き起こすか、意図しない動作をする可能性がありました。
この問題はGoのIssue #5591として報告されており、Plan 9環境でのビルドの互換性を確保するために、Plan 9のCコンパイラが認識するnilというキーワード(またはマクロ)を使用するように修正されました。これは、Go言語が多様なプラットフォームでの動作をサポートするための継続的な努力の一環です。
前提知識の解説
Plan 9 from Bell Labs
Plan 9 from Bell Labsは、ベル研究所で開発された分散オペレーティングシステムです。Unixの後継として設計され、Unixの概念をさらに推し進めたもので、特に「すべてがファイルである」という原則を徹底し、ネットワーク透過性を重視しています。Go言語の設計者であるRob Pike、Ken Thompson、Russ Coxらは、Plan 9の開発にも深く関わっており、Go言語の設計思想やツールチェインにはPlan 9の影響が色濃く反映されています。
Plan 9のCコンパイラは、標準Cとは異なる独自の拡張や慣習を持っていました。例えば、ポインタのヌル値を表現するためにNULLではなくnilというキーワード(またはマクロ)を使用することが一般的でした。これは、Go言語のnil(ポインタ、インターフェース、スライス、マップ、チャネルなどのゼロ値)とは直接的な関係はありませんが、Plan 9のCコードにおけるヌル値の表現方法が、このコミットの背景にある問題を引き起こしました。
Go言語のツールチェインとリンカ (5l, 6l, 8l)
Go言語のビルドシステムは、コンパイラ、アセンブラ、リンカなどから構成されるツールチェインです。このコミットで言及されている5l, 6l, 8lは、それぞれ異なるアーキテクチャ向けのリンカを指します。
5l: ARMアーキテクチャ向けのリンカ。6l: x86-64(AMD64)アーキテクチャ向けのリンカ。8l: x86(32ビット)アーキテクチャ向けのリンカ。
これらのリンカは、コンパイラによって生成されたオブジェクトファイル(.oファイル)を結合し、実行可能なバイナリを生成する役割を担っています。Goのツールチェインは、クロスコンパイルを容易にするように設計されており、異なるアーキテクチャやOS向けのバイナリを生成できます。
C言語におけるNULLとPlan 9におけるnil
C言語の標準では、ヌルポインタ定数は通常NULLというマクロで定義されます。これは、0または(void*)0に展開されることが一般的です。しかし、Plan 9のCコンパイラ環境では、ヌルポインタ定数としてnilが使用される慣習がありました。これは、Plan 9のCコンパイラがNULLを認識しないか、あるいは異なる意味で解釈する可能性があったためです。
この違いは、GoのツールチェインのC言語で書かれた部分が、Plan 9のCコンパイラでビルドされる際に互換性の問題を引き起こしました。Goのコードベースは、初期にはPlan 9のCコンパイラでビルドされることを想定していたため、このような特定の環境に依存する記述が存在していました。
技術的詳細
このコミットは、Go言語のリンカのソースコード(C言語で記述)におけるヌルポインタの比較方法を修正しています。問題の箇所は、cursym->ptrs == NULLという条件式です。ここでcursymはシンボルに関する情報を保持する構造体であり、ptrsはそのシンボルに関連付けられたポインタマップデータへのポインタです。
リンカは、プログラム内のシンボル(関数、変数など)に関する情報を処理し、それらをメモリ上の適切な位置に配置します。ポインタマップデータは、ガベージコレクション(GC)のために、どのメモリ領域がポインタを含んでいるかをリンカが把握するために使用されます。cursym->nptrs == -1は、ポインタマップデータが存在しないことを示す特殊な値であり、cursym->ptrs == NULL(またはnil)は、ポインタ自体がヌルであることをチェックしています。
Plan 9環境でビルドする際、C言語の標準的なNULLマクロが正しく解釈されないため、コンパイルエラーや予期せぬランタイムエラーが発生する可能性がありました。このため、Plan 9のCコンパイラが認識するnilに置き換えることで、Plan 9環境でのビルドが成功し、リンカが正しく動作するようになります。
この修正は、Goのツールチェインが特定のビルド環境(この場合はPlan 9)に依存している部分を特定し、その環境での互換性を確保するための具体的な対応策です。Go言語はクロスプラットフォーム開発を重視しているため、このような環境固有の差異を吸収する修正は非常に重要です。
コアとなるコードの変更箇所
変更は、Go言語のリンカのソースコードである以下の3つのファイルにわたっています。
src/cmd/5l/obj.csrc/cmd/6l/obj.csrc/cmd/8l/obj.c
それぞれのファイルにおいて、cursym->ptrs == NULLという比較がcursym->ptrs == nilに置き換えられています。
--- a/src/cmd/5l/obj.c
+++ b/src/cmd/5l/obj.c
@@ -647,7 +647,7 @@ loop:
case APTRS:
if(skip)
goto casedef;
- if(cursym->nptrs == -1 || cursym->ptrs == NULL) {
+ if(cursym->nptrs == -1 || cursym->ptrs == nil) {
diag("ldobj1: pointer map data provided for %s without a definition", cursym->name);
errorexit();
}
コアとなるコードの解説
変更されたコードスニペットは、リンカのldobj1関数(またはそれに相当するロジック)の一部であり、シンボルのポインタマップデータを処理するAPTRSケース内にあります。
if(cursym->nptrs == -1 || cursym->ptrs == nil) {
diag("ldobj1: pointer map data provided for %s without a definition", cursym->name);
errorexit();
}
cursym: 現在処理中のシンボルを表す構造体へのポインタです。cursym->nptrs: シンボルに関連付けられたポインタの数を示します。-1は、ポインタマップデータが提供されていないことを示す特殊な値として使用されています。cursym->ptrs: シンボルに関連付けられたポインタマップデータへのポインタです。このポインタがnil(以前はNULL)であるかどうかをチェックしています。
この条件式は、「もしシンボルにポインタマップデータが提供されていない(nptrs == -1)か、またはポインタマップデータへのポインタがヌルである(ptrs == nil)ならば」というロジックを表しています。このような状況は、リンカがポインタマップデータを期待しているにもかかわらず、それが欠落していることを意味するため、診断メッセージを出力してプログラムを終了(errorexit())します。
この修正の目的は、Plan 9環境でのビルド時にNULLが正しく解釈されない問題を回避し、リンカがすべてのサポート対象環境で一貫して動作するようにすることです。NULLをnilに置き換えることで、Plan 9のCコンパイラがこのヌルポインタ定数を正しく認識し、コンパイルが成功するようになります。
関連リンク
- Go Issue #5591: https://github.com/golang/go/issues/5591
- Go Code Review (CL) 9839046: https://golang.org/cl/9839046
参考にした情報源リンク
- Plan 9 from Bell Labs: https://9p.io/plan9/
- Go言語のツールチェインに関する一般的な情報 (Go公式ドキュメントなど): https://go.dev/doc/
- C言語におけるNULLマクロの定義: https://en.cppreference.com/w/c/types/NULL
- Go言語の
nilについて: https://go.dev/tour/basics/12 (Go Tourのnilに関するセクション) - Goのリンカに関する情報 (Goのソースコードや関連する設計ドキュメント): https://go.dev/src/cmd/link/