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

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

このコミットは、Go言語のリンカ (cmd/ld) におけるPlan 9環境での型シグネチャの非互換性を修正するものです。具体的には、ELF (Executable and Linkable Format) 生成に関連する変更がPlan 9コンパイラで問題を引き起こしたため、lib.h ヘッダーファイルに #pragma incomplete struct Elf64_Shdr ディレクティブを追加することで、この問題を解決しています。

コミット

commit 74c03cb81491a2e7e6559b6cf92518287260ce36
Author: Anthony Martin <ality@pbrane.org>
Date:   Wed Jan 9 15:05:22 2013 -0800

    cmd/ld: fix incompatible type signatures on Plan 9
    
    Changeset f483bfe81114 moved ELF generation to the architecture
    independent code and in doing so added a Section* to the Sym
    type and an Elf64_Shdr* to the Section type.
    
    This caused the Plan 9 compilers to complain about incompatible
    type signatures in the many files that reference the Sym type.
    
    R=rsc, dave
    CC=golang-dev
    https://golang.org/cl/7057058

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

https://github.com/golang/go/commit/74c03cb81491a2e7e6559b6cf92518287260ce36

元コミット内容

このコミットは、Goリンカ (cmd/ld) がPlan 9オペレーティングシステム上でコンパイルされる際に発生する型シグネチャの非互換性を修正します。以前の変更セット f483bfe81114 (ELF生成をアーキテクチャ非依存のコードに移動したもの) が原因で、Sym 型に Section* が、Section 型に Elf64_Shdr* が追加されました。これにより、Sym 型を参照する多くのファイルでPlan 9コンパイラが型シグネチャの非互換性について不平を言うようになりました。このコミットは、src/cmd/ld/lib.h#pragma incomplete struct Elf64_Shdr を追加することで、この問題を解決しています。

変更の背景

Go言語のリンカ (cmd/ld) は、コンパイルされたGoパッケージと依存関係を単一の実行可能バイナリに結合する役割を担っています。Go言語は、その共同作成者であるRob PikeとKen ThompsonがPlan 9オペレーティングシステムの開発にも深く関わっていたため、Plan 9と歴史的・アーキテクチャ的に密接な関係があります。Goのツールチェイン、特にコンパイラとアセンブラは、Plan 9のツールチェインから直接派生しています。

コミットメッセージによると、以前の変更セット f483bfe81114 がELF (Executable and Linkable Format) 生成のロジックをアーキテクチャ非依存のコードに移行しました。この移行の一環として、リンカ内部で使用されるデータ構造である Sym 型に Section* ポインタが、そして Section 型に Elf64_Shdr* ポインタが追加されました。

しかし、この変更がPlan 9環境で問題を引き起こしました。Plan 9コンパイラは、これらの新しいポインタ型が追加された Sym 型を参照する際に、型シグネチャの非互換性を検出してエラーを報告しました。これは、Elf64_Shdr 構造体の完全な定義が、その型が使用されるすべての場所で利用可能ではなかったためと考えられます。C言語では、構造体の完全な定義がない場合でも、その構造体へのポインタを宣言することは可能ですが、その構造体のメンバーにアクセスしたり、sizeof 演算子を使用したりすることはできません。Plan 9コンパイラは、この「不完全な型」の扱いに関して、より厳格なチェックを行っていた可能性があります。

このコミットは、このPlan 9コンパイラのエラーを解消し、GoリンカがPlan 9環境でも正しくビルドできるようにするために導入されました。

前提知識の解説

Go言語のリンカ (cmd/ld)

Go言語のビルドプロセスにおいて、リンカは非常に重要な役割を担っています。go build コマンドによって自動的に呼び出される cmd/ld (または cmd/link) は、コンパイルされたGoパッケージ(オブジェクトファイル)と、それらが依存するライブラリ(静的ライブラリや共有ライブラリ)を結合し、最終的な実行可能バイナリを生成します。その主な機能は以下の通りです。

  • シンボル解決: 異なるコンパイル済みパッケージ間で参照される関数や変数の定義を見つけ、それらのアドレスを解決します。
  • 再配置: コードやデータセグメント内のメモリアドレスを調整し、最終的な実行可能ファイル内で正しい位置に配置されるようにします。
  • デッドコード排除: 使用されていないコードやデータを最終バイナリから削除し、ファイルサイズを削減します。
  • 実行可能ファイルの生成: ターゲットオペレーティングシステム(例: LinuxのELF、WindowsのPE)に適した形式で実行可能ファイルを生成します。

Plan 9 オペレーティングシステム

Plan 9 from Bell Labsは、ベル研究所で開発された分散型オペレーティングシステムです。Go言語の共同作成者であるRob PikeとKen Thompsonがその開発に深く関わっており、Go言語の設計思想やツールチェインに大きな影響を与えています。Plan 9は、すべてのリソースをファイルとして表現するというユニークな設計哲学を持ち、シンプルさ、モジュール性、ネットワーク透過性を重視しています。Go言語のツールチェイン、特にコンパイラとアセンブラは、Plan 9のツールチェインから直接派生しており、GoのアセンブラはPlan 9のアセンブラの構文スタイルを使用しています。

ELF (Executable and Linkable Format)

ELFは、Unix系オペレーティングシステム(Linux、Solaris、FreeBSDなど)で広く使用されている、実行可能ファイル、オブジェクトコード、共有ライブラリ、コアダンプの標準ファイル形式です。ELF形式は、プログラムの実行に必要なすべての情報(コード、データ、シンボルテーブル、再配置情報など)を構造化して格納します。リンカは、ELF形式のオブジェクトファイルを結合し、最終的なELF形式の実行可能ファイルを生成します。

#pragma incomplete struct

#pragma ディレクティブは、C/C++言語において、コンパイラ固有の拡張機能を提供するために使用されます。これは標準C/C++の一部ではなく、コンパイラによってその動作が異なります。

#pragma incomplete struct SomeStruct のような形式は、特定のコンパイラが「不完全な型」の扱いに関して特別な指示を必要とする場合に使用される可能性があります。

不完全な型 (Incomplete Type) とは、C言語において、宣言はされているものの、コンパイラがそのサイズやメモリレイアウトを決定するのに十分な情報がまだない型を指します。構造体の場合、これは通常、メンバーの定義がまだ提供されていない前方宣言 (struct SomeStruct;) を意味します。

不完全な型へのポインタ (struct SomeStruct *ptr;) は宣言できますが、以下の操作はできません。

  • 不完全な型の変数を宣言する (struct SomeStruct my_struct;)。
  • 不完全な型に対して sizeof_Alignof 演算子を使用する。
  • 不完全な型へのポインタを逆参照する(メンバーにアクセスする)。

このコミットの文脈では、Elf64_Shdr 構造体の完全な定義が、Sym 型や Section 型が参照されるすべての場所で利用可能ではなかったため、Plan 9コンパイラがエラーを発生させたと考えられます。#pragma incomplete struct Elf64_Shdr は、Plan 9コンパイラに対して、Elf64_Shdr が不完全な型であることを明示的に伝え、それに関連する厳格な型チェックを緩和または調整するよう指示していると推測されます。これにより、コンパイラは Elf64_Shdr* ポインタの宣言をエラーとせずに受け入れることができるようになります。

技術的詳細

このコミットの技術的な核心は、Goリンカの内部データ構造と、異なるオペレーティングシステム(特にPlan 9)のコンパイラがC言語の型定義をどのように解釈するかの間の相互作用にあります。

Goリンカは、C言語で書かれた部分とGo言語で書かれた部分が混在しています。src/cmd/ld/lib.h は、リンカのC言語部分で使用されるヘッダーファイルであり、リンカの主要なデータ構造(Sym, Section, Segment など)の定義を含んでいます。

以前の変更セット f483bfe81114 は、ELF生成ロジックをアーキテクチャ非依存にするために、これらのデータ構造に変更を加えました。具体的には、Sym 構造体に Section* 型のメンバーが追加され、Section 構造体に Elf64_Shdr* 型のメンバーが追加されました。Elf64_Shdr は、ELFファイル内のセクションヘッダーテーブルのエントリを表す構造体であり、ELF形式のバイナリを扱う上で不可欠なものです。

問題は、Elf64_Shdr 構造体の完全な定義が、lib.h をインクルードするすべてのCソースファイルで常に利用可能であるとは限らなかった点にあります。C言語のコンパイラは、ポインタの宣言自体は、そのポインタが指す型の完全な定義がなくても許可します(前方宣言 struct SomeStruct; があれば十分)。しかし、Plan 9のCコンパイラは、この点に関してより厳格なルールを持っていたか、あるいは特定のコンテキストで Elf64_Shdr の前方宣言が不足していたために、型シグネチャの非互換性エラーを報告したと考えられます。

このコミットで追加された #pragma incomplete struct Elf64_Shdr は、Plan 9コンパイラに対する特定の指示です。このプラグマは、コンパイラに対して Elf64_Shdr が不完全な構造体型であることを明示的に伝え、その型へのポインタの使用に関する特定の警告やエラーを抑制するよう指示します。これにより、コンパイラは Section 構造体内の Elf64_Shdr* メンバーの宣言を、Elf64_Shdr の完全な定義がまだ利用できない場合でも、エラーとせずに処理できるようになります。

これは、クロスプラットフォーム開発においてよく見られる問題の一例です。あるプラットフォーム(この場合はPlan 9)のコンパイラが、別のプラットフォーム(この場合はELFを生成する一般的なUnix系システム)では問題にならないような、C言語の標準の特定の解釈や拡張に対して厳格である場合に発生します。この修正は、GoリンカのコードベースがPlan 9環境でも問題なくコンパイルされるようにするための、プラットフォーム固有の調整と言えます。

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

変更は src/cmd/ld/lib.h ファイルにのみ行われています。

--- a/src/cmd/ld/lib.h
+++ b/src/cmd/ld/lib.h
@@ -97,6 +97,8 @@ struct Segment
 	Section*	sect;
 };
 
+#pragma incomplete struct Elf64_Shdr
+
 struct Section
 {
 	uchar	rwx;

追加された行は以下の通りです。

#pragma incomplete struct Elf64_Shdr

コアとなるコードの解説

src/cmd/ld/lib.h は、GoリンカのC言語部分で共有されるヘッダーファイルです。このファイルには、リンカがオブジェクトファイルや実行可能ファイルを処理する際に使用する主要なデータ構造の定義が含まれています。

追加された #pragma incomplete struct Elf64_Shdr は、Section 構造体の定義の直前に配置されています。Section 構造体は、リンカが処理するバイナリ内のセクション(コードセクション、データセクションなど)を表します。コミットメッセージによると、この Section 構造体には Elf64_Shdr* 型のメンバーが追加されています。

このプラグマの目的は、Plan 9コンパイラに対して、Elf64_Shdr が不完全な型であることを明示的に伝えることです。これにより、Section 構造体内で Elf64_Shdr* ポインタが宣言されていても、Elf64_Shdr の完全な定義が lib.h をインクルードするすべてのCソースファイルで利用可能でなくても、コンパイラが型シグネチャの非互換性エラーを発生させないようにします。

これは、Plan 9コンパイラの特定の挙動に対応するための、プラットフォーム固有のコンパイラディレクティブです。他のシステム(Linux、macOSなど)のGCCやClangのようなコンパイラでは、通常、このようなプラグマは必要ありません。これは、それらのコンパイラが不完全な型へのポインタ宣言をより寛容に扱うためです。

この修正は、Goリンカのコードベースの移植性を高め、Plan 9という特定の環境でのビルド問題を解決するために不可欠でした。

関連リンク

参考にした情報源リンク