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

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

このコミットは、Go言語のリンカである cmd/ldsrc/cmd/ld/pe.c ファイルに影響を与えます。pe.c は、Windowsの実行可能ファイル形式であるPortable Executable (PE) フォーマットに関連する処理を扱う部分です。具体的には、PEファイルのイメージベースアドレスを示すシンボルの定義に関する修正が行われています。

コミット

commit 60513bf2f4c465adafe45fae639a77a2c94ac07b
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Mon Sep 23 13:08:00 2013 -0400

    cmd/ld: fix "_image_base__ not defined" problem for cmd/8l.
    Fixes #6431.
    
    Change suggested by kin.wilson.za.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/13314048

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

https://github.com/golang/go/commit/60513bf2f4c465adafe45fae639a77a2c94ac07b

元コミット内容

--- a/src/cmd/ld/pe.c
+++ b/src/cmd/ld/pe.c
@@ -151,6 +151,7 @@ peinit(void)
 
 	// some mingw libs depend on this symbol, for example, FindPESectionByName
 	xdefine("__image_base__", SDATA, PEBASE);
+	xdefine("_image_base__", SDATA, PEBASE);
 }
 
 static void

変更の背景

このコミットは、GoプログラムをWindows向けにビルドする際に、MinGW (Minimalist GNU for Windows) でコンパイルされたライブラリを使用すると発生する「_image_base__ が未定義」というリンカエラーを修正するために導入されました。

Goリンカ (cmd/ld) は、WindowsのPEフォーマットの実行可能ファイルを生成する際に、__image_base__ というシンボルを定義していました。このシンボルは、PEファイルがメモリにロードされる際の推奨ベースアドレスを示す重要なものです。しかし、一部のMinGWライブラリ、特に古いバージョンや特定のコンパイル設定では、このシンボルを _image_base__ (アンダースコアが1つ少ない) という名前で参照していました。

この名前の不一致により、Goリンカが生成した実行可能ファイルとMinGWライブラリをリンクしようとすると、MinGWライブラリが期待する _image_base__ シンボルが見つからず、リンカエラーが発生していました。コミットメッセージにある #6431 は、この問題が報告されたGoのIssueトラッカーのエントリを指しています。

前提知識の解説

Goリンカ (cmd/ld)

Goリンカ (cmd/ld) は、Goツールチェーンの重要なコンポーネントであり、Goプログラムのビルドプロセスにおいて、コンパイルされたGoのオブジェクトファイルやアーカイブファイル、およびそれらの依存関係を結合して単一の実行可能バイナリを生成する役割を担っています。このプロセスには、シンボルの解決やアドレスの割り当てが含まれ、プログラムの各部分が互いに正しく参照できるようにします。

かつてGoには、8l (32-bit x86向け)、6l (64-bit x86向け)、5l (ARM向け) のように、アーキテクチャ固有のリンカが存在しました。しかし、Goツールチェーンの進化に伴い、これらの個別のコマンドは抽象化され、現在では go buildgo install といった主要な go コマンドによって内部的に呼び出される形になっています。cmd/ld は、これらのアーキテクチャ固有のリンカの機能を統合し、Goプログラムのビルドを統一的に処理する役割を担っています。

Portable Executable (PE) フォーマット

Portable Executable (PE) フォーマットは、Windowsオペレーティングシステムで使用される実行可能ファイル、DLL (Dynamic Link Library)、OBJ (オブジェクトファイル) などの標準ファイル形式です。PEファイルは、プログラムコード、データ、リソース、メタデータなどを構造化された形式で格納しており、Windowsローダーがプログラムをメモリにロードし、実行するために必要な情報を提供します。

PEフォーマットの重要な要素の一つに「イメージベースアドレス」があります。これは、PEファイルがメモリにロードされる際の推奨される仮想メモリ開始アドレスを示す値です。PEヘッダ内の ImageBase フィールドに格納されます。オペレーティングシステムは、この推奨アドレスにファイルをロードしようとしますが、ASLR (Address Space Layout Randomization) などのセキュリティ機能や、他のモジュールとのアドレス衝突により、異なるアドレスにロードされることもあります。その場合、リンカによって生成されたリロケーション情報に基づいて、プログラム内のアドレスが実際のロードアドレスに合わせて調整されます。

MinGW

MinGW (Minimalist GNU for Windows) は、Windows上でGNU開発ツール(GCCコンパイラ、GNU Binutilsなど)を使用できるようにする開発環境です。これにより、Windowsネイティブアプリケーションを、LinuxなどのUnix系システムで慣れ親しんだツールチェーンを使って開発することができます。MinGWでコンパイルされたライブラリは、WindowsのPEフォーマットに準拠していますが、GNUツールチェーンの特性上、特定のシンボル命名規則やリンカの挙動に違いが生じることがあります。

特に、__ImageBase シンボルに関しては、GNU ld リンカはバージョン2.19以降で標準的な __ImageBase を提供していますが、それ以前は _image_base__ という独自のシンボルを使用していた時期がありました。この歴史的な経緯が、Goリンカとの互換性問題の一因となりました。

シンボル定義 (xdefine)

xdefine は、Goリンカの内部関数であり、リンカがシンボルを定義し、管理するために使用されます。シンボルは、変数、関数、セクションなどのプログラム要素の名前と、それらがメモリ内でどこに配置されるかを示すアドレス情報を関連付けるものです。リンカは、これらのシンボル情報を用いて、コンパイルされた複数のオブジェクトファイルやライブラリを結合し、最終的な実行可能バイナリを生成します。

xdefine 関数は、特定のシンボル名、そのシンボルが属するセクション(例: SDATA は初期化済みデータセクション)、およびそのシンボルの値(例: PEBASE はPEイメージのベースアドレス)を指定して、リンカのシンボルテーブルにエントリを追加します。

技術的詳細

この問題の核心は、WindowsのPEフォーマットにおいてイメージベースアドレスを示すシンボルに、GoリンカとMinGWライブラリの間で命名規則の不一致があったことです。

  1. Goリンカの挙動: 修正前のGoリンカ (cmd/ld) は、src/cmd/ld/pe.c 内で xdefine("__image_base__", SDATA, PEBASE); という行を通じて、__image_base__ (アンダースコアが2つ) というシンボルを定義していました。これは、WindowsのPEフォーマットにおける標準的な慣習に沿ったものでした。
  2. MinGWライブラリの期待: しかし、一部のMinGWでコンパイルされたライブラリ、特に古いバージョンや特定のビルド設定では、イメージベースアドレスを _image_base__ (アンダースコアが1つ) という名前で参照するように設計されていました。これは、MinGWのGNU ld リンカが過去にこの命名規則を使用していたことに起因します。例えば、FindPESectionByName のような関数がこのシンボルに依存していました。
  3. リンカエラーの発生: Goリンカが __image_base__ のみを定義し、_image_base__ を定義していなかったため、GoプログラムがMinGWライブラリを使用しようとすると、MinGWライブラリが期待する _image_base__ シンボルが見つからず、リンカが「_image_base__ not defined」というエラーを報告してビルドが失敗していました。

このコミットは、Goリンカが両方のシンボル名を定義することで、この互換性の問題を解決します。これにより、Goでビルドされた実行可能ファイルが、__image_base__ を期待する新しいMinGWライブラリと、_image_base__ を期待する古いMinGWライブラリの両方と正しくリンクできるようになります。

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

変更は src/cmd/ld/pe.c ファイルの peinit 関数内の一行追加です。

 	// some mingw libs depend on this symbol, for example, FindPESectionByName
 	xdefine("__image_base__", SDATA, PEBASE);
+	xdefine("_image_base__", SDATA, PEBASE);
 }

コアとなるコードの解説

peinit 関数は、PEフォーマットの初期化処理を行う部分です。この関数内で、GoリンカはPEファイルに必要な特定のシンボルを定義しています。

追加された行 xdefine("_image_base__", SDATA, PEBASE); は、以下のことを行っています。

  • xdefine: リンカの内部関数で、新しいシンボルを定義するために使用されます。
  • "_image_base__": 定義するシンボルの名前です。これが、MinGWライブラリが期待するアンダースコアが1つのバージョンです。
  • SDATA: シンボルが初期化済みデータセクションに属することを示します。
  • PEBASE: シンボルの値として、PEイメージのベースアドレスを割り当てます。

この一行を追加することで、Goリンカは __image_base___image_base__ の両方のシンボルを定義するようになります。これにより、MinGWライブラリがどちらの命名規則でイメージベースアドレスを参照していても、Goリンカが生成する実行可能ファイルと正しくリンクできるようになり、リンカエラーが解消されます。

関連リンク

  • Go CL (Code Review): https://golang.org/cl/13314048
  • Go Issue 6431: (Web検索では直接的なIssueページは見つかりませんでしたが、コミットメッセージで参照されているため、GoのIssueトラッカーに存在すると考えられます。)

参考にした情報源リンク