[インデックス 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アーキテクチャに依存する部分を持つことを意味します。
このようなアーキテクチャ固有の定数の使用は、以下のような問題を引き起こす可能性があります。
- 移植性の低下: 新しいアーキテクチャをサポートする際に、既存のオブジェクトファイルフォーマットの定義を更新する必要があり、複雑さが増します。
- メンテナンスの複雑化: 各アーキテクチャのリンカコードが、それぞれ異なる数値定数を扱うため、コードの重複や理解の困難さにつながります。
- 将来的な互換性の問題: オブジェクトファイルフォーマットは、一度広く採用されると変更が非常に困難になります(コミットメッセージにある「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.type
とAuto.type
で使用されていたアーキテクチャ固有の数値定数を、アーキテクチャに依存しない新しい列挙型に置き換えたことです。
具体的には、以下の変更が行われました。
-
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_AUTO
とA_PARAM
という新しい列挙型が導入されました。これらは、自動変数が通常のローカル変数(A_AUTO
)であるか、関数のパラメータ(A_PARAM
)であるかを示します。- 以前は
LinkArch
構造体内にアーキテクチャ固有のD_ADDR
,D_PCREL
,D_SIZE
,D_AUTO
,D_PARAM
などのフィールドがありましたが、これらは削除され、新しい汎用的なR_
およびA_
プレフィックスの列挙型に置き換えられました。
-
リンカコードにおける定数の置き換え:
src/cmd/{5l,6l,8l}/asm.c
、src/cmd/ld/data.c
、src/cmd/ld/dwarf.c
、src/cmd/ld/ldmacho.c
、src/cmd/ld/ldpe.c
、src/cmd/ld/lib.c
、src/cmd/link/load.go
、src/cmd/link/pclntab.go
、src/liblink/asm5.c
、src/liblink/asm6.c
、src/liblink/asm8.c
、src/liblink/data.c
、src/liblink/obj5.c
、src/liblink/obj6.c
、src/liblink/obj8.c
、src/liblink/objfile.c
など、Goリンカの様々な部分で、古いD_
プレフィックスのアーキテクチャ固有の定数が、新しいR_
またはA_
プレフィックスの汎用的な列挙型に置き換えられました。- これにより、リンカのコードは、特定のリロケーションや自動変数の型を扱う際に、アーキテクチャに依存しない共通の識別子を使用するようになります。例えば、ARMリンカ(
5l
)ではD_CALL
がR_CALL
に、x86-64リンカ(6l
)ではD_PCREL
がR_PCREL
に、x86リンカ(8l
)ではD_ADDR
がR_ADDR
にそれぞれ変更されています。
-
オブジェクトファイルフォーマットのポータブル化:
- コミットメッセージにあるように、この変更により、Goの新しいオブジェクトファイルフォーマットに残っていたアーキテクチャ固有のビットがほぼすべて削除されました。これにより、異なるアーキテクチャ間でオブジェクトファイルをより容易に共有・処理できるようになり、Goのクロスコンパイル能力がさらに強化されます。
この変更は、Goのツールチェインの内部的なクリーンアップと、将来的な拡張性およびメンテナンス性の向上に大きく貢献します。特に、Go 1.3という重要なリリースを前にこの変更を行うことで、ファイルフォーマットの安定性を早期に確立しようとする意図が伺えます。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更箇所は、主に以下のファイルに集中しています。
-
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;
-
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 ```
-
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.type
とAuto.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 linker and object files (Go 1.2のリリースノート、リンカとオブジェクトファイルに関する初期の言及)
- Go linker design document (Goリンカの設計に関するREADMEファイル)
- Go言語の変更リスト(Gerrit):
- https://golang.org/cl/87670044 (このコミットのGerritレビューページ)
参考にした情報源リンク
- Go言語の公式ソースコードリポジトリ: https://github.com/golang/go
- ELF (Executable and Linkable Format) の仕様書 (一般的な情報源): https://refspecs.linuxfoundation.org/elf/elf.pdf
- Mach-O の仕様書 (一般的な情報源): Apple Developer Documentation
- Portable Executable (PE) の仕様書 (一般的な情報源): Microsoft Docs
- Go言語のリリースノート (特にGo 1.3以前のリンカ関連の変更): https://go.dev/doc/devel/release
- Go言語のリンカに関するブログ記事や解説 (一般的な情報源):
- "Go's Linker" by Russ Cox (2012): https://go.dev/blog/go1.2#linker (Go 1.2のリンカに関するブログ記事)
- "A Tour of Go's Tooling" by Dave Cheney: https://dave.cheney.net/2014/09/14/a-tour-of-gos-tooling (Goツールチェイン全般に関する記事)
- Go言語のリンカのソースコード内のコメント
- Go言語のコミュニティフォーラムやメーリングリストでの議論 (一般的な情報源)
- Go言語のリンカの内部構造に関する解説記事
これらの情報源は、Goリンカの動作、オブジェクトファイルフォーマット、およびリロケーションの概念を理解する上で役立ちました。特に、Goの公式ドキュメントとソースコード内のコメントは、このコミットの背景と技術的詳細を深く掘り下げる上で不可欠でした。