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

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

このコミットは、Go言語のリンカ(liblink)におけるオブジェクトファイルフォーマットから、アーキテクチャ固有の定数を削除し、よりポータブルな列挙型に置き換える変更を詳述しています。これにより、Goのオブジェクトファイルフォーマットの将来的な互換性と移植性が向上します。

コミット

commit 8d39e55c6516be5ee3267b8ce101b324a4f09986
Author: Russ Cox <rsc@golang.org>
Date:   Mon Apr 14 15:54:20 2014 -0400

    liblink: remove arch-specific constants from file format
    
    The relocation and automatic variable types were using
    arch-specific numbers. Introduce portable enumerations
    instead.
    
    To the best of my knowledge, these are the only arch-specific
    bits left in the new object file format.
    
    Remove now, before Go 1.3, because file formats are forever.
    
    LGTM=iant
    R=iant
    CC=golang-codereviews
    https://golang.org/cl/87670044

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

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

元コミット内容

liblink: remove arch-specific constants from file format

The relocation and automatic variable types were using
arch-specific numbers. Introduce portable enumerations
instead.

To the best of my knowledge, these are the only arch-specific
bits left in the new object file format.

Remove now, before Go 1.3, because file formats are forever.

LGTM=iant
R=iant
CC=golang-codereviews
https://golang.org/cl/87670044

変更の背景

Go言語のツールチェイン、特にリンカ(liblink)は、コンパイルされたコードを最終的な実行可能ファイルに結合する役割を担っています。このプロセスにおいて、オブジェクトファイル(コンパイルされたソースコードの中間表現)は、シンボル、リロケーション情報、自動変数(スタック変数)の型など、様々なメタデータを含んでいます。

このコミット以前は、リロケーションタイプ(Reloc.type)や自動変数の型(Auto.type)を識別するために、アーキテクチャ(例: ARM、x86、x86-64)ごとに異なる数値定数が使用されていました。これは、Goのオブジェクトファイルフォーマットが、特定のCPUアーキテクチャに依存する部分を持つことを意味します。

このようなアーキテクチャ固有の定数の使用は、以下のような問題を引き起こす可能性があります。

  1. 移植性の低下: 新しいアーキテクチャをサポートする際に、既存のオブジェクトファイルフォーマットの定義を更新する必要があり、複雑さが増します。
  2. メンテナンスの複雑化: 各アーキテクチャのリンカコードが、それぞれ異なる数値定数を扱うため、コードの重複や理解の困難さにつながります。
  3. 将来的な互換性の問題: オブジェクトファイルフォーマットは、一度広く採用されると変更が非常に困難になります(コミットメッセージにある「file formats are forever」という言葉がこれを強調しています)。Go 1.3のリリース前にこの問題を解決することで、将来的なフォーマットの安定性を確保しようとしています。

このコミットは、これらの問題を解決し、Goのオブジェクトファイルフォーマットをより堅牢で、アーキテクチャに依存しないものにすることを目的としています。

前提知識の解説

このコミットを理解するためには、以下の概念について基本的な知識が必要です。

  • リンカ (Linker): コンパイラによって生成された複数のオブジェクトファイルやライブラリを結合し、実行可能なプログラムや共有ライブラリを作成するツールです。Go言語では、cmd/link(以前はliblink)がこの役割を担っています。
  • オブジェクトファイル (Object File): ソースコードがコンパイルされた後の中間ファイルです。機械語コード、データ、シンボルテーブル、リロケーション情報などが含まれます。
  • リロケーション (Relocation): オブジェクトファイル内のコードやデータが、最終的な実行可能ファイル内のどこに配置されるかによってアドレスが変化する場合に、そのアドレスを修正するプロセスです。例えば、関数呼び出しのアドレスやグローバル変数の参照アドレスなどがこれに該当します。リロケーションタイプは、どのような種類のアドレス修正が必要かを示します。
  • 自動変数 (Automatic Variables): 関数内で宣言され、その関数の実行中にのみ存在するローカル変数です。通常、スタック上に割り当てられます。
  • シンボル (Symbol): プログラム内の関数や変数などの名前付きエンティティを識別するためのものです。シンボルテーブルは、これらのシンボルとそのアドレスや型などの情報を含みます。
  • include/link.h: Goリンカの内部で使用される共通のデータ構造や定数が定義されているヘッダファイルです。
  • src/cmd/{5l,6l,8l}/asm.c: それぞれARM (5l), x86-64 (6l), x86 (8l) アーキテクチャ向けのリンカのアセンブリコード生成部分です。これらのファイルは、特定のリロケーションタイプを処理するロジックを含んでいます。
  • src/cmd/link/load.go: オブジェクトファイルをロードし、リロケーションを適用するGo言語のコードです。
  • ELF (Executable and Linkable Format): LinuxなどのUnix系システムで広く使われている実行可能ファイル、オブジェクトファイル、共有ライブラリの標準フォーマットです。
  • Mach-O: macOSやiOSで使われている実行可能ファイル、オブジェクトファイル、共有ライブラリのフォーマットです。
  • PE (Portable Executable): Windowsで使われている実行可能ファイル、オブジェクトファイル、DLLのフォーマットです。
  • PLT (Procedure Linkage Table): 共有ライブラリ内の関数を呼び出す際に使用されるテーブルです。動的リンカが実行時に実際の関数のアドレスを解決するために利用します。
  • GOT (Global Offset Table): 共有ライブラリ内のグローバル変数や関数のアドレスを格納するテーブルです。PLTと同様に、動的リンカが実行時にアドレスを解決するために利用します。
  • TLS (Thread Local Storage): 各スレッドが独自のコピーを持つ変数です。

技術的詳細

このコミットの主要な変更点は、Reloc.typeAuto.typeで使用されていたアーキテクチャ固有の数値定数を、アーキテクチャに依存しない新しい列挙型に置き換えたことです。

具体的には、以下の変更が行われました。

  1. include/link.hにおける新しい列挙型の導入:

    • Reloc.typeのために、R_ADDR, R_SIZE, R_CALL, R_CONST, R_PCREL, R_TLS, R_GOTOFF, R_PLT0, R_PLT1, R_PLT2といった新しい列挙型が導入されました。これらは、それぞれ特定のリロケーションの種類(アドレス、サイズ、関数呼び出し、定数、PC相対、TLS、GOTオフセット、PLTエントリなど)を表します。
    • Auto.typeのために、A_AUTOA_PARAMという新しい列挙型が導入されました。これらは、自動変数が通常のローカル変数(A_AUTO)であるか、関数のパラメータ(A_PARAM)であるかを示します。
    • 以前はLinkArch構造体内にアーキテクチャ固有のD_ADDR, D_PCREL, D_SIZE, D_AUTO, D_PARAMなどのフィールドがありましたが、これらは削除され、新しい汎用的なR_およびA_プレフィックスの列挙型に置き換えられました。
  2. リンカコードにおける定数の置き換え:

    • src/cmd/{5l,6l,8l}/asm.csrc/cmd/ld/data.csrc/cmd/ld/dwarf.csrc/cmd/ld/ldmacho.csrc/cmd/ld/ldpe.csrc/cmd/ld/lib.csrc/cmd/link/load.gosrc/cmd/link/pclntab.gosrc/liblink/asm5.csrc/liblink/asm6.csrc/liblink/asm8.csrc/liblink/data.csrc/liblink/obj5.csrc/liblink/obj6.csrc/liblink/obj8.csrc/liblink/objfile.cなど、Goリンカの様々な部分で、古いD_プレフィックスのアーキテクチャ固有の定数が、新しいR_またはA_プレフィックスの汎用的な列挙型に置き換えられました。
    • これにより、リンカのコードは、特定のリロケーションや自動変数の型を扱う際に、アーキテクチャに依存しない共通の識別子を使用するようになります。例えば、ARMリンカ(5l)ではD_CALLR_CALLに、x86-64リンカ(6l)ではD_PCRELR_PCRELに、x86リンカ(8l)ではD_ADDRR_ADDRにそれぞれ変更されています。
  3. オブジェクトファイルフォーマットのポータブル化:

    • コミットメッセージにあるように、この変更により、Goの新しいオブジェクトファイルフォーマットに残っていたアーキテクチャ固有のビットがほぼすべて削除されました。これにより、異なるアーキテクチャ間でオブジェクトファイルをより容易に共有・処理できるようになり、Goのクロスコンパイル能力がさらに強化されます。

この変更は、Goのツールチェインの内部的なクリーンアップと、将来的な拡張性およびメンテナンス性の向上に大きく貢献します。特に、Go 1.3という重要なリリースを前にこの変更を行うことで、ファイルフォーマットの安定性を早期に確立しようとする意図が伺えます。

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

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

  1. include/link.h: 新しい列挙型が定義された場所です。

    --- a/include/link.h
    +++ b/include/link.h
    @@ -223,6 +223,28 @@ enum
     	SHIDDEN = 1<<9, // hidden or local symbol
     };
     
    +// Reloc.type
    +enum
    +{
    +	R_ADDR = 1,
    +	R_SIZE,
    +	R_CALL,
    +	R_CONST,
    +	R_PCREL,
    +	R_TLS,
    +	R_GOTOFF,
    +	R_PLT0,
    +	R_PLT1,
    +	R_PLT2,
    +};
    +
    +// Auto.type
    +enum
    +{
    +	A_AUTO = 1,
    +	A_PARAM,
    +};
    +
     struct	Auto
     {
     	LSym*\tasym;
    @@ -420,14 +442,14 @@ struct LinkArch
     	
     	// TODO: Give these the same values on all systems.
     	int	D_ADDR;
    +	int	D_AUTO;
     	int	D_BRANCH;
     	int	D_CONST;
     	int	D_EXTERN;
     	int	D_FCONST;
     	int	D_NONE;
    -	int	D_PCREL;
    +	int	D_PARAM;
     	int	D_SCONST;
    -	int	D_SIZE;
     	int	D_STATIC;
     
     	int	ACALL;
    
  2. src/cmd/5l/asm.c (ARMリンカの例): リロケーションタイプがD_からR_に置き換えられている箇所です。

    --- a/src/cmd/5l/asm.c
    +++ b/src/cmd/5l/asm.c
    @@ -102,7 +102,7 @@ adddynrel(LSym *s, Reloc *r)\n 
     	// Handle relocations found in ELF object files.\n     	case 256 + R_ARM_PLT32:\n-\t\tr->type = D_CALL;\n+\t\tr->type = R_CALL;\n     	\tif(targ->type == SDYNIMPORT) {\n     	\t\taddpltsym(ctxt, targ);\n     	\t\tr->sym = linklookup(ctxt, \".plt\", 0);\n    ```
    
    
  3. src/cmd/link/load.go: Go言語側でリロケーションタイプを扱う部分の定数定義が変更されています。

    --- a/src/cmd/link/load.go
    +++ b/src/cmd/link/load.go
    @@ -73,17 +73,11 @@ func (p *Prog) loadPackage(pkg *Package) {
     	}
     }
     
    -// TODO(rsc): These are the relocation types and should be
    -// loaded from debug/goobj. They are not in debug/goobj
    -// because they are different for each architecture.\n-// The symbol file format needs to be revised to use an\n-// architecture-independent set of numbers, and then\n-// those should be fetched from debug/goobj instead of\n-// defined here. These are the amd64 numbers.\n+// TODO(rsc): Define full enumeration for relocation types.\n const (\n-\tD_ADDR  = 120\n-\tD_SIZE  = 246\n-\tD_PCREL = 247\n+\tR_ADDR  = 1\n+\tR_SIZE  = 2\n+\tR_PCREL = 5\n )\n     
     // relocateSym applies relocations to sym\'s data.\n     @@ -99,9 +93,9 @@ func (p *Prog) relocateSym(sym *Sym, data []byte) {\n     	\tswitch r.Type {\n     	\tdefault:\n     	\t\tp.errorf(\"%v: unknown relocation type %d\", sym, r.Type)\n-\t\tcase D_ADDR:\n+\t\tcase R_ADDR:\n     	\t\t// ok\n-\t\tcase D_PCREL:\n+\t\tcase R_PCREL:\n     	\t\tval -= sym.Addr + Addr(r.Offset+r.Size)\n     	\t}\n     	\tfrag := data[r.Offset : r.Offset+r.Size]\
    

コアとなるコードの解説

include/link.h

このファイルは、Goリンカのオブジェクトファイルフォーマットの基盤となる定義を含んでいます。変更前は、LinkArch構造体内にD_ADDR, D_PCREL, D_SIZE, D_AUTO, D_PARAMといったアーキテクチャ固有の整数定数が定義されていました。これらの定数は、各アーキテクチャのリンカがリロケーションや自動変数の型を識別するために使用していました。

このコミットでは、これらのアーキテクチャ固有の定数を削除し、代わりにReloc.typeAuto.typeという2つの新しいenum(列挙型)を導入しました。

  • Reloc.type:

    • R_ADDR: 絶対アドレスリロケーション。
    • R_SIZE: サイズリロケーション。
    • R_CALL: 関数呼び出しリロケーション。
    • R_CONST: 定数リロケーション。
    • R_PCREL: プログラムカウンタ相対リロケーション。
    • R_TLS: スレッドローカルストレージリロケーション。
    • R_GOTOFF: GOT (Global Offset Table) オフセットリロケーション。
    • R_PLT0, R_PLT1, R_PLT2: PLT (Procedure Linkage Table) エントリに関連する特定のリロケーション。これらはARMアーキテクチャのPLTエントリ生成で使われる3つの命令に対応しています。
  • Auto.type:

    • A_AUTO: 通常の自動変数(ローカル変数)。
    • A_PARAM: 関数のパラメータ。

これらの新しい列挙型は、特定のアーキテクチャに依存しない共通のセマンティクスを持つため、Goのオブジェクトファイルフォーマットがよりポータブルになります。リンカの各アーキテクチャ固有の部分は、これらの共通の列挙値を使用してリロケーションや自動変数の型を処理するようになります。

src/cmd/5l/asm.c (および他のasm.cファイル)

src/cmd/5l/asm.cはARMアーキテクチャ向けのリンカのアセンブリコード生成部分です。このファイルでは、ELFオブジェクトファイルから読み取られたリロケーションタイプを、Goリンカ内部のReloc.typeにマッピングする処理が行われています。

変更前は、例えばR_ARM_PLT32というELFリロケーションタイプが、Goリンカ内部のD_CALLというアーキテクチャ固有の定数にマッピングされていました。このコミットでは、このマッピングがR_CALLという新しい汎用的な列挙型に変更されています。

同様の変更が、x86-64 (src/cmd/6l/asm.c) や x86 (src/cmd/8l/asm.c) などの他のアーキテクチャ固有のリンカファイルでも行われています。これにより、各アーキテクチャのリンカは、Goのオブジェクトファイルフォーマットとやり取りする際に、共通のR_プレフィックスの列挙型を使用するようになります。これは、リンカのコードベース全体の整合性を高め、新しいアーキテクチャの追加や既存のアーキテクチャの変更が容易になることを意味します。

src/cmd/link/load.go

src/cmd/link/load.goは、Goのオブジェクトファイルをロードし、その中のリロケーション情報を処理するGo言語のコードです。このファイルには、以前はD_ADDR, D_SIZE, D_PCRELといった定数が定義されていました。これらは、Goのオブジェクトファイルフォーマットから読み取られたリロケーションタイプをGo言語のコードで扱うためのものでした。

このコミットでは、これらの定数が削除され、代わりにR_ADDR, R_SIZE, R_PCRELという新しい列挙型が導入されました。これにより、Go言語で書かれたリンカの共通部分も、アーキテクチャに依存しないリロケーションタイプを使用するようになります。relocateSym関数など、リロケーションを実際に適用するロジックは、これらの新しいR_プレフィックスの列挙型に基づいて動作します。

これらの変更は、Goのリンカがオブジェクトファイルフォーマットを扱う方法を根本的に改善し、Goのクロスプラットフォーム開発能力をさらに強化するための重要なステップです。

関連リンク

  • Go言語のリンカに関する公式ドキュメント(Goのバージョンによって内容が異なる場合があります):
  • Go言語の変更リスト(Gerrit):

参考にした情報源リンク

これらの情報源は、Goリンカの動作、オブジェクトファイルフォーマット、およびリロケーションの概念を理解する上で役立ちました。特に、Goの公式ドキュメントとソースコード内のコメントは、このコミットの背景と技術的詳細を深く掘り下げる上で不可欠でした。