[インデックス 14099] ファイルの概要
このコミットは、Go言語のARMアーキテクチャ向けリンカであるcmd/5l
が、FreeBSDオペレーティングシステムと互換性のあるELF(Executable and Linkable Format)バイナリを生成できるようにするための変更を導入しています。具体的には、動的リンクに関連するセクションの初期化と配置、およびELFヘッダの設定をFreeBSDの要件に合わせて修正しています。
コミット
commit d901808869db4236f436e067e4bd957de7d54595
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Wed Oct 10 01:02:49 2012 +0800
cmd/5l: generate FreeBSD compatible ELF
1. correctly initialize .plt.got entries (point to the 1st entry)
2. add section .rel.plt (FreeBSD insists PLT relocs to be there)
3. put relocs of .got.plt into .rel.plt
4. set ELFOSABI_FREEBSD in ELF header
R=rsc
CC=golang-dev
https://golang.org/cl/6643050
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d901808869db4236f436e067e4bd957de7d54595
元コミット内容
cmd/5l: generate FreeBSD compatible ELF
.plt.got
エントリを正しく初期化する(最初のエントリを指すようにする).rel.plt
セクションを追加する(FreeBSDはPLTのリロケーションがそこにあることを要求する).got.plt
のリロケーションを.rel.plt
に配置する- ELFヘッダに
ELFOSABI_FREEBSD
を設定する
変更の背景
この変更の背景には、Go言語がFreeBSD/ARM環境で生成する実行ファイルが、FreeBSDの動的リンカの特定の要件を満たしていなかったという問題があります。特に、ELFバイナリの動的リンクメカニズム(PLT/GOT)の挙動や、リロケーション情報の配置に関して、LinuxとFreeBSDの間で差異が存在しました。
Goのリンカcmd/5l
は、これまで主にLinux環境でのELF生成を念頭に置いて設計されていましたが、FreeBSD上でのGoプログラムの実行を可能にするためには、これらのOS固有の差異に対応する必要がありました。具体的には、.plt.got
エントリの初期化方法、.rel.plt
セクションの存在、そしてELFヘッダ内のOS ABI(Application Binary Interface)識別子の設定が、FreeBSDの動的リンカが期待する形式と異なっていたため、実行時エラーやリンクエラーが発生する可能性がありました。
このコミットは、これらの互換性の問題を解決し、GoでビルドされたARMバイナリがFreeBSD上で正しく動的リンクされ、実行されるようにすることを目的としています。
前提知識の解説
このコミットを理解するためには、以下の概念についての前提知識が必要です。
- ELF (Executable and Linkable Format): Unix系システムで広く使われている実行ファイル、共有ライブラリ、オブジェクトファイルの標準フォーマットです。プログラムのコード、データ、メタデータがどのように配置されるかを定義します。
- 動的リンク (Dynamic Linking): プログラムが実行時に必要なライブラリをロードし、リンクする仕組みです。これにより、ディスクスペースの節約やメモリの効率的な利用が可能になります。
- PLT (Procedure Linkage Table): 動的リンクされた関数を呼び出す際に使用されるテーブルです。プログラムが外部関数を初めて呼び出す際に、動的リンカがその関数の実際のアドレスを解決し、PLTエントリを更新します。
- GOT (Global Offset Table): 動的リンクされたデータや関数のアドレスを格納するテーブルです。PLTはGOTと連携して動作し、外部関数のアドレスを間接的に参照します。
- .got.plt: GOTの一部で、特にPLTから参照されるエントリを格納します。
- .rel.plt (Relocation section for PLT): PLTエントリのリロケーション情報(どこをどのように修正すべきか)を格納するセクションです。一部のOS(FreeBSDなど)では、PLTに関連するリロケーション情報がこの専用セクションに存在することを期待します。
- ELFヘッダ (ELF Header): ELFファイルの先頭にある構造体で、ファイルのタイプ(実行ファイル、共有ライブラリなど)、ターゲットアーキテクチャ、OS ABIなどの基本的な情報を含みます。
- OS ABI (Operating System Application Binary Interface): 特定のオペレーティングシステムとアーキテクチャの組み合わせにおけるバイナリ互換性のルールセットです。
EI_OSABI
フィールドは、そのELFファイルがどのOSのABIに準拠しているかを示します。ELFOSABI_FREEBSD
は、FreeBSDのABIに準拠していることを示します。 - リンカ (Linker): コンパイラによって生成されたオブジェクトファイルやライブラリを結合し、実行可能なプログラムや共有ライブラリを作成するツールです。
cmd/5l
はGo言語のARMアーキテクチャ向けリンカです。 ld-linux.so.3
/ld-elf.so.1
: これらは動的リンカ(ダイナミックローダー)のパスです。Linuxではld-linux.so
が、FreeBSDではld-elf.so
が一般的に使用されます。実行ファイルが起動される際に、これらのローダーがプログラムと必要な共有ライブラリをメモリにロードし、動的リンクを解決します。
技術的詳細
このコミットは、GoのARMリンカcmd/5l
がFreeBSD互換のELFバイナリを生成するために、以下の主要な技術的変更を加えています。
-
.plt.got
エントリの初期化の修正:- 従来のLinux環境では、
.got.plt
エントリの初期化は動的リンカに任されていました。しかし、FreeBSD/ARMの動的リンカは、GOTエントリが最初のPLTエントリを指すように自動的に初期化しないため、リンカ自身がこの初期化を行う必要がありました。 src/cmd/5l/asm.c
のaddpltsym
関数において、シンボルのGOTエントリ(s->got
)にplt
の先頭アドレス(plt->size
が0の場合)をaddaddrplus
で追加するように変更されました。これにより、各GOTエントリが対応するPLTエントリの先頭を指すようになります。
- 従来のLinux環境では、
-
.rel.plt
セクションの追加とリロケーションの移動:- FreeBSDは、PLTに関連するリロケーション情報が専用の
.rel.plt
セクションに存在することを強く要求します。従来のGoリンカは、これらのリロケーションを他のリロケーションセクション(例:.rel
)に含めていた可能性があります。 src/cmd/5l/asm.c
のasmb
関数内で、SHT_REL
タイプの新しいセクションとして.rel.plt
が追加されました。このセクションは、.plt
セクションのリロケーション情報を格納します。.got.plt
に関連するリロケーションも、この新しく追加された.rel.plt
セクションに配置されるように変更されました。これにより、FreeBSDの動的リンカが期待する形式でリロケーション情報が提供されます。
- FreeBSDは、PLTに関連するリロケーション情報が専用の
-
ELFヘッダの
EI_OSABI
フィールドの設定:- ELFヘッダの
e_ident
配列内のEI_OSABI
フィールドは、バイナリがどのOSのABIに準拠しているかを示します。FreeBSDのバイナリは、このフィールドがELFOSABI_FREEBSD
(値は9)に設定されていることを期待します。 src/cmd/5l/asm.c
のasmb
関数において、HEADTYPE
がHfreebsd
の場合に、eh->ident[EI_OSABI]
がELFOSABI_FREEBSD
に設定されるように修正されました。これにより、FreeBSDのシステムがGoによって生成されたバイナリを正しく認識し、処理できるようになります。
- ELFヘッダの
-
動的リンカパスの指定:
- FreeBSD環境では、動的リンカのパスがLinuxとは異なります。
src/cmd/5l/asm.c
にfreebsddynld[] = "/usr/libexec/ld-elf.so.1";
が追加され、HEADTYPE
がHfreebsd
の場合にこのパスがインタープリタとして使用されるようにelfinterp
の呼び出しが修正されました。
-
セクションの順序とリンカの挙動:
src/cmd/5l/asm.c
のasmb
関数内で、動的リンク関連のセクション(.dynsym
,.dynstr
,.rel.plt
,.plt
,.got.plt
など)の生成順序が調整されました。特に、.rel.plt
が追加されたことで、既存のセクションインデックスやリンカの内部ロジックに影響が出ないように、関連するelftextsh
の値も調整されています。src/cmd/5l/obj.c
では、Hfreebsd
という新しいヘッダタイプが認識され、main
関数でgoos
に基づいてデフォルトのHEADTYPE
が設定されるようになりました。これにより、GoのビルドシステムがFreeBSDターゲットを正しく扱えるようになります。
これらの変更により、GoのARMリンカはFreeBSDの動的リンクのセマンティクスに適合し、GoプログラムがFreeBSD/ARM環境で期待通りに実行されるようになります。
コアとなるコードの変更箇所
-
src/cmd/5l/asm.c
:freebsddynld
変数の追加:/usr/libexec/ld-elf.so.1
addpltsym
関数:.got.plt
エントリの初期化ロジックを修正し、addaddrplus(got, plt, 0)
で最初のPLTエントリを指すように変更。asmb
関数:elftextsh
のインクリメントを+9
から+10
に変更。Elfput
ラベルの導入と、HEADTYPE
がデフォルトの場合のgoto Elfput
。- インタープリタパスの選択ロジックに
Hfreebsd
を追加し、freebsddynld
を使用するように変更。 - 動的リンクセクションの生成順序を変更し、
.rel.plt
セクション(SHT_REL
タイプ)を新しく追加。このセクションは.plt
のリロケーション情報を格納する。 - ELFヘッダの
EI_OSABI
フィールドをHfreebsd
の場合にELFOSABI_FREEBSD
に設定。
-
src/cmd/5l/obj.c
:headers
配列に"freebsd", Hfreebsd
を追加し、Hfreebsd
を認識できるようにする。main
関数で、HEADTYPE
が未設定の場合にheadtype(goos)
を呼び出してOSに応じたヘッダタイプを設定するように変更。これにより、FreeBSD環境でビルドする際に自動的にHfreebsd
が選択されるようになる。Hfreebsd
の場合もdebug['d'] = 0
(動的リンクを有効にする)を設定。
コアとなるコードの解説
-
src/cmd/5l/asm.c
のaddpltsym
関数における変更:// .got entry s->got = got->size; // In theory, all GOT should point to the first PLT entry, // Linux/ARM's dynamic linker will do that for us, but FreeBSD/ARM's // dynamic linker won't, so we'd better do it ourselves. addaddrplus(got, plt, 0);
この部分が、FreeBSD/ARMの動的リンカの挙動の違いに対応する核心的な変更です。Linuxでは動的リンカが自動的にGOTエントリを初期化しますが、FreeBSDではリンカ自身が明示的に最初のPLTエントリを指すように設定する必要があります。
addaddrplus(got, plt, 0)
は、.got.plt
セクションに、.plt
セクションの開始アドレス(オフセット0)を指すエントリを追加します。これにより、動的リンカがシンボルを解決する前に、GOTエントリが正しい初期状態を持つことが保証されます。 -
src/cmd/5l/asm.c
のasmb
関数における.rel.plt
セクションの追加:sh = newElfShdr(elfstr[ElfStrRelPlt]); sh->type = SHT_REL; sh->flags = SHF_ALLOC; sh->entsize = ELF32RELSIZE; sh->addralign = 4; sh->link = dynsym; sh->info = eh->shnum; // .plt shsym(sh, lookup(".rel.plt", 0));
このコードブロックは、FreeBSDが要求する
.rel.plt
セクションをELFファイルに追加します。SHT_REL
タイプはリロケーションセクションであることを示し、sh->link = dynsym
は関連するシンボルテーブル(.dynsym
)へのリンクを設定します。sh->info = eh->shnum
は、このリロケーションが適用されるセクション(この場合は.plt
セクション)のインデックスを示します。これにより、PLTに関連するリロケーション情報がFreeBSDの動的リンカが期待する専用の場所に配置されます。 -
src/cmd/5l/asm.c
のasmb
関数におけるEI_OSABI
の設定:switch(HEADTYPE) { case Hfreebsd: eh->ident[EI_OSABI] = ELFOSABI_FREEBSD; break; }
このスニペットは、ELFヘッダの
EI_OSABI
フィールドを、ターゲットOSがFreeBSDである場合にELFOSABI_FREEBSD
に設定します。これは、FreeBSDシステムがバイナリを正しく識別し、そのABIに合わせた処理を行うために不可欠です。 -
src/cmd/5l/obj.c
のmain
関数におけるHEADTYPE
の自動設定:if(HEADTYPE == -1) HEADTYPE = headtype(goos);
この変更により、リンカはコンパイル対象のOS(
goos
変数で指定される)に基づいて、適切なELFヘッダタイプ(Hlinux
またはHfreebsd
など)を自動的に選択するようになります。これにより、クロスコンパイルや特定のOS向けビルドがよりシームレスになります。
これらの変更は、GoのリンカがFreeBSDのELFバイナリの仕様に厳密に準拠し、GoプログラムがFreeBSD/ARM環境でネイティブに動作するための基盤を確立します。
関連リンク
- ELF (Executable and Linkable Format) - Wikipedia: https://ja.wikipedia.org/wiki/Executable_and_Linkable_Format
- Procedure Linkage Table - Wikipedia: https://en.wikipedia.org/wiki/Procedure_Linkage_Table
- Global Offset Table - Wikipedia: https://en.wikipedia.org/wiki/Global_Offset_Table
- Go言語のリンカに関するドキュメントやソースコード(Goの公式ドキュメントやGoのGitHubリポジトリ内)
参考にした情報源リンク
- Go言語のソースコード (特に
src/cmd/5l
ディレクトリ) - ELF Specification (System V Application Binary Interface)
- FreeBSDのELFに関するドキュメントやmanページ (例:
elf(5)
) - 動的リンクに関する一般的な技術記事や書籍