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

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

このコミットは、Go言語のビルドシステムとリンカにおけるWindows環境でのCGO(C言語との相互運用)に関する問題を修正するものです。具体的には、src/cmd/go/build.gosrc/cmd/ld/pe.c、およびsrc/run.batの3つのファイルが変更されています。build.goではlibgccのリンク順序が調整され、pe.cではWindowsのPE(Portable Executable)ファイル形式において特定のシンボル__image_base__が定義され、run.batではCGOテストの実行が再度有効化されています。

コミット

  • コミットハッシュ: 5490814c3058f4cab756e353b8855e1ba155cdb3
  • Author: Shenghou Ma minux.ma@gmail.com
  • Date: Wed Sep 26 22:34:25 2012 +0800

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

https://github.com/golang/go/commit/5490814c3058f4cab756e353b8855e1ba155cdb3

元コミット内容

    cmd/go, cmd/ld: fix libgcc order and add __image_base__ symbol for windows
            Fixes #4063.
    
    R=alex.brainman, rsc
    CC=golang-dev
    https://golang.org/cl/6543066

変更の背景

このコミットは、Go issue #4063「misc/cgo/life fails on Windows go builder」を修正するために行われました。この問題は、Windows環境でCGOを使用する際に、特にmisc/cgo/lifeというテストが失敗するというものでした。

具体的な問題点は以下の2つに集約されます。

  1. libgccのリンク順序の問題: Windows環境でCGOを使ってC/C++のコードをGoプログラムにリンクする際、MinGW(Minimalist GNU for Windows)が提供するライブラリ(libmingwex, libmingw32など)とlibgccのリンク順序が正しくないと、シンボルが見つからないなどのリンクエラーが発生していました。特に、libmingwexlibmingw32自体がlibgccに依存している場合、libgccが先にリンクされると、これらのライブラリがlibgccのシンボルを見つけられず、問題が生じます。
  2. __image_base__シンボルの欠如: Windowsの実行可能ファイル(PEファイル)では、__image_base__というシンボルが、実行ファイルのロードアドレス(ベースアドレス)を示すために一部のライブラリ(特にMinGWのランタイムライブラリの一部)で内部的に使用されることがあります。Goのリンカ(cmd/ld)が生成するPEファイルには、このシンボルが明示的に定義されていなかったため、このシンボルに依存するC/C++ライブラリが正しく動作しない、あるいはリンクに失敗するという問題が発生していました。FindPESectionByNameのような関数がこのシンボルに依存していることが示唆されています。

これらの問題により、Windows上でのCGOの安定性が損なわれ、特に自動ビルド環境(Go builder)でのテストが失敗していました。このコミットは、これらの問題を解決し、Windows環境でのCGOの信頼性を向上させることを目的としています。

前提知識の解説

CGO (C Go)

CGOは、GoプログラムからC言語のコードを呼び出したり、C言語のコードからGoの関数を呼び出したりするためのGoの機能です。これにより、既存のCライブラリをGoプロジェクトで再利用したり、パフォーマンスが重要な部分をCで記述したりすることが可能になります。CGOを使用すると、GoコンパイラはCコードをコンパイルし、Goコードとリンクするための適切なコマンドを生成します。

cmd/go

cmd/goはGoのビルドツールチェーンの主要なコマンドです。ソースコードのコンパイル、パッケージの管理、テストの実行など、Goプロジェクトのビルドプロセス全体を管理します。CGOを使用する場合、cmd/goはCコンパイラ(通常はGCC)を呼び出し、Cコードをオブジェクトファイルにコンパイルし、Goのオブジェクトファイルとリンクする役割を担います。

cmd/ld

cmd/ldはGoのリンカです。コンパイルされたGoのオブジェクトファイルと、CGOによって生成されたCのオブジェクトファイル、および必要な外部ライブラリ(libgcc, libmingwexなど)を結合して、最終的な実行可能ファイルや共有ライブラリを生成します。Windows環境では、PE(Portable Executable)形式の実行ファイルを生成します。

libgcc

libgccは、GCC(GNU Compiler Collection)が提供するランタイムサポートライブラリです。C/C++のコードが特定のハードウェア命令や複雑な算術演算(例: 64ビット整数除算、浮動小数点演算)を使用する場合、または例外処理などのランタイム機能を利用する場合に必要となる補助関数やルーチンが含まれています。libgccは、コンパイラが生成するコードが依存する低レベルの機能を実装しています。

MinGW (Minimalist GNU for Windows)

MinGWは、Windows上でGCCツールチェーンを使用するための環境を提供するプロジェクトです。これにより、Windowsネイティブな実行可能ファイルをGCCでコンパイルできます。MinGWには、GCCコンパイラだけでなく、Windows APIを呼び出すためのヘッダファイルやライブラリ(libmingwex, libmingw32など)も含まれています。

libmingwexlibmingw32

これらはMinGWが提供するライブラリの一部です。

  • libmingw32: MinGWの基本的なランタイムライブラリで、Windows APIへのラッパーや、標準Cライブラリの一部機能を提供します。
  • libmingwex: MinGWの拡張ライブラリで、追加のユーティリティ関数や、より高度なWindows APIの機能を提供することがあります。

これらのライブラリは、C/C++プログラムがWindows上で動作するために必要な多くの低レベル機能を提供し、しばしばlibgccに依存しています。

PE (Portable Executable) フォーマット

PEフォーマットは、Windowsオペレーティングシステムで使用される実行可能ファイル、オブジェクトコード、DLL(Dynamic Link Library)などのファイル形式です。PEファイルは、ヘッダ、セクション、リソースなどの構造を持ち、プログラムの実行に必要な情報(コード、データ、インポート/エクスポートテーブルなど)を含んでいます。

__image_base__ シンボル

__image_base__は、WindowsのPEファイルにおいて、実行可能ファイルやDLLがメモリにロードされる際のベースアドレス(先頭アドレス)を示すために使用されるシンボルです。一部のコンパイラやリンカ、特にMinGW環境で生成されたコードやライブラリは、このシンボルを内部的に参照して、相対アドレスを絶対アドレスに変換したり、特定のメモリ領域を特定したりすることがあります。このシンボルが正しく定義されていないと、これらのライブラリが期待通りに動作しない可能性があります。

技術的詳細

このコミットの技術的詳細は、Windows環境におけるCGOのリンキングプロセスにおける2つの主要な問題点とその解決策に焦点を当てています。

  1. libgccのリンク順序の修正:

    • 問題: 従来のGoのビルドプロセスでは、Windows環境でCGOを使用する際に、libgcclibmingwexlibmingw32よりも先にリンクされていました。しかし、libmingwexlibmingw32といったMinGWのライブラリは、内部的にlibgccが提供する関数に依存している場合があります。リンカは通常、コマンドラインで指定された順序でライブラリを解決しようとします。もし依存関係のあるライブラリが、その依存先のライブラリよりも先に処理されると、リンカは必要なシンボルを見つけることができず、未解決のシンボルエラー("undefined reference")を報告します。
    • 解決策: src/cmd/go/build.goの変更により、Windows環境でのCGOビルドにおいて、libmingwexlibmingw32libgccよりも先にリンクするように順序が変更されました。具体的には、リンカに渡すライブラリのリストが[]string{"-lmingwex", "-lmingw32", libgcc}という順序になりました。これにより、libmingwexlibmingw32libgccのシンボルを必要とする際に、libgccが既にリンカによって処理される状態になり、シンボル解決が正しく行われるようになります。これは、リンカの動作原理(依存関係のあるライブラリは、その依存先よりも後に指定されるべき)に合致する修正です。
  2. __image_base__シンボルの追加:

    • 問題: WindowsのPEファイル形式では、実行可能ファイルやDLLのロードアドレスを示す__image_base__というシンボルが、一部のMinGWライブラリ(例: FindPESectionByNameのような関数)によって内部的に参照されることがあります。Goのリンカ(cmd/ld)が生成するPEファイルには、このシンボルが明示的に定義されていませんでした。このシンボルが欠如していると、それに依存するC/C++ライブラリが正しく初期化されなかったり、実行時にクラッシュしたりする原因となっていました。
    • 解決策: src/cmd/ld/pe.cpeinit関数に、__image_base__シンボルを定義するコードが追加されました。具体的には、xdefine("__image_base__", SDATA, PEBASE);という行が追加されています。
      • xdefine: リンカがシンボルを定義するための内部関数です。
      • "__image_base__": 定義するシンボルの名前です。
      • SDATA: シンボルがデータセクションに関連付けられていることを示します。
      • PEBASE: PEファイルのベースアドレス(ロードアドレス)を示す定数です。 この変更により、Goリンカが生成するPEファイルに__image_base__シンボルが正しく埋め込まれるようになり、このシンボルに依存する外部C/C++ライブラリがWindows環境で期待通りに動作するようになります。

これらの修正は、Windows環境におけるGoのCGO機能の堅牢性と互換性を大幅に向上させ、特にMinGWベースのツールチェーンとの連携をスムーズにすることを目的としています。

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

src/cmd/go/build.go

--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -1597,9 +1597,12 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo,\
 	if err != nil {
 		return nil, nil, err
 	}
-	staticLibs := []string{libgcc}
+	var staticLibs []string
 	if goos == "windows" {
-		staticLibs = append(staticLibs, "-lmingwex", "-lmingw32")
+		// libmingw32 and libmingwex might also use libgcc, so libgcc must come last
+		staticLibs = []string{"-lmingwex", "-lmingw32", libgcc}
+	} else {
+		staticLibs = []string{libgcc}
 	}
 
 	for _, cfile := range cfiles {

src/cmd/ld/pe.c

--- a/src/cmd/ld/pe.c
+++ b/src/cmd/ld/pe.c
@@ -148,6 +148,9 @@ peinit(void)
 	PESECTHEADR = rnd(PEFILEHEADR, PESECTALIGN);
 	nextsectoff = PESECTHEADR;
 	nextfileoff = PEFILEHEADR;
+\
+\t// some mingw libs depend on this symbol, for example, FindPESectionByName
+\txdefine("__image_base__", SDATA, PEBASE);\
 }
 
 static void

src/run.bat

--- a/src/run.bat
+++ b/src/run.bat
@@ -64,11 +64,10 @@ echo.
 
 :: cgo tests
 if x%CGO_ENABLED% == x0 goto nocgo
-:: TODO(brainman) disabled, because it is broken on go builder - http://golang.org/issue/4063
-::echo # ..\misc\cgo\life
-::go run %GOROOT%\\test\\run.go - ..\misc\cgo\life
-::if errorlevel 1 goto fail
-::echo.
+echo # ..\misc\cgo\life
+go run %GOROOT%\\test\\run.go - ..\misc\cgo\life
+if errorlevel 1 goto fail
+echo.
 
 echo # ..\misc\cgo\stdio
 go run %GOROOT%\\test\\run.go - ..\misc\cgo\\stdio

コアとなるコードの解説

src/cmd/go/build.goの変更

この変更は、Goのビルドツール(cmd/go)がCGOプログラムをリンクする際に、外部ライブラリをリンカに渡す順序を調整しています。

  • 元のコードでは、staticLibsの初期値としてlibgccのみが設定され、Windowsの場合に"-lmingwex", "-lmingw32"appendされていました。これは、libgccが最初にリストに追加され、その後にMinGWライブラリが続くことを意味します。
  • 変更後のコードでは、goos == "windows"の場合にstaticLibs[]string{"-lmingwex", "-lmingw32", libgcc}と明示的に再定義されています。これにより、libmingwexlibmingw32libgccよりも先にリンカに渡されるようになります。
  • コメント// libmingw32 and libmingwex might also use libgcc, so libgcc must come lastが追加されており、MinGWライブラリがlibgccに依存しているため、libgccを最後にリンクする必要があるという理由が明確にされています。これは、リンカが依存関係を正しく解決するために必要な順序です。

src/cmd/ld/pe.cの変更

この変更は、Goのリンカ(cmd/ld)がWindowsのPEファイルを生成する際に、特定のシンボル__image_base__を定義するようにしています。

  • peinit()関数は、PEファイルの初期化処理を行う部分です。
  • 追加された行xdefine("__image_base__", SDATA, PEBASE);は、__image_base__という名前のシンボルを定義しています。
    • SDATAは、このシンボルがデータセクションに関連付けられていることを示します。
    • PEBASEは、PEファイルのベースアドレス(メモリにロードされる際の先頭アドレス)を表す定数です。
  • コメント// some mingw libs depend on this symbol, for example, FindPESectionByNameが追加されており、MinGWライブラリの一部がこのシンボルに依存していることが示されています。この修正により、MinGWライブラリが__image_base__を正しく参照できるようになり、Windows上でのCGOプログラムの安定性が向上します。

src/run.batの変更

この変更は、Windows環境でのGoのテストスクリプトにおいて、CGO関連のテストの実行を再度有効にしています。

  • 元のコードでは、misc\cgo\lifeテストの実行がコメントアウトされており、TODO(brainman) disabled, because it is broken on go builder - http://golang.org/issue/4063というコメントが付いていました。これは、Go issue #4063で報告された問題のために、このテストが一時的に無効化されていたことを示しています。
  • 今回のコミットで、上記のbuild.gope.cの修正により問題が解決されたため、このテストのコメントアウトが解除され、再び実行されるようになりました。これにより、CGOの修正が正しく機能していることを自動テストで検証できるようになります。

関連リンク

参考にした情報源リンク