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

[インデックス 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

  1. .plt.gotエントリを正しく初期化する(最初のエントリを指すようにする)
  2. .rel.pltセクションを追加する(FreeBSDはPLTのリロケーションがそこにあることを要求する)
  3. .got.pltのリロケーションを.rel.pltに配置する
  4. 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バイナリを生成するために、以下の主要な技術的変更を加えています。

  1. .plt.gotエントリの初期化の修正:

    • 従来のLinux環境では、.got.pltエントリの初期化は動的リンカに任されていました。しかし、FreeBSD/ARMの動的リンカは、GOTエントリが最初のPLTエントリを指すように自動的に初期化しないため、リンカ自身がこの初期化を行う必要がありました。
    • src/cmd/5l/asm.caddpltsym関数において、シンボルのGOTエントリ(s->got)にpltの先頭アドレス(plt->sizeが0の場合)をaddaddrplusで追加するように変更されました。これにより、各GOTエントリが対応するPLTエントリの先頭を指すようになります。
  2. .rel.pltセクションの追加とリロケーションの移動:

    • FreeBSDは、PLTに関連するリロケーション情報が専用の.rel.pltセクションに存在することを強く要求します。従来のGoリンカは、これらのリロケーションを他のリロケーションセクション(例: .rel)に含めていた可能性があります。
    • src/cmd/5l/asm.casmb関数内で、SHT_RELタイプの新しいセクションとして.rel.pltが追加されました。このセクションは、.pltセクションのリロケーション情報を格納します。
    • .got.pltに関連するリロケーションも、この新しく追加された.rel.pltセクションに配置されるように変更されました。これにより、FreeBSDの動的リンカが期待する形式でリロケーション情報が提供されます。
  3. ELFヘッダのEI_OSABIフィールドの設定:

    • ELFヘッダのe_ident配列内のEI_OSABIフィールドは、バイナリがどのOSのABIに準拠しているかを示します。FreeBSDのバイナリは、このフィールドがELFOSABI_FREEBSD(値は9)に設定されていることを期待します。
    • src/cmd/5l/asm.casmb関数において、HEADTYPEHfreebsdの場合に、eh->ident[EI_OSABI]ELFOSABI_FREEBSDに設定されるように修正されました。これにより、FreeBSDのシステムがGoによって生成されたバイナリを正しく認識し、処理できるようになります。
  4. 動的リンカパスの指定:

    • FreeBSD環境では、動的リンカのパスがLinuxとは異なります。
    • src/cmd/5l/asm.cfreebsddynld[] = "/usr/libexec/ld-elf.so.1";が追加され、HEADTYPEHfreebsdの場合にこのパスがインタープリタとして使用されるようにelfinterpの呼び出しが修正されました。
  5. セクションの順序とリンカの挙動:

    • src/cmd/5l/asm.casmb関数内で、動的リンク関連のセクション(.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(動的リンクを有効にする)を設定。

コアとなるコードの解説

  1. src/cmd/5l/asm.caddpltsym 関数における変更:

    // .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エントリが正しい初期状態を持つことが保証されます。

  2. src/cmd/5l/asm.casmb 関数における .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の動的リンカが期待する専用の場所に配置されます。

  3. src/cmd/5l/asm.casmb 関数における EI_OSABI の設定:

    switch(HEADTYPE) {
    case Hfreebsd:
        eh->ident[EI_OSABI] = ELFOSABI_FREEBSD;
        break;
    }
    

    このスニペットは、ELFヘッダのEI_OSABIフィールドを、ターゲットOSがFreeBSDである場合にELFOSABI_FREEBSDに設定します。これは、FreeBSDシステムがバイナリを正しく識別し、そのABIに合わせた処理を行うために不可欠です。

  4. src/cmd/5l/obj.cmain 関数における HEADTYPE の自動設定:

    if(HEADTYPE == -1)
        HEADTYPE = headtype(goos);
    

    この変更により、リンカはコンパイル対象のOS(goos変数で指定される)に基づいて、適切なELFヘッダタイプ(HlinuxまたはHfreebsdなど)を自動的に選択するようになります。これにより、クロスコンパイルや特定のOS向けビルドがよりシームレスになります。

これらの変更は、GoのリンカがFreeBSDのELFバイナリの仕様に厳密に準拠し、GoプログラムがFreeBSD/ARM環境でネイティブに動作するための基盤を確立します。

関連リンク

参考にした情報源リンク

  • Go言語のソースコード (特に src/cmd/5l ディレクトリ)
  • ELF Specification (System V Application Binary Interface)
  • FreeBSDのELFに関するドキュメントやmanページ (例: elf(5))
  • 動的リンクに関する一般的な技術記事や書籍