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

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

このコミットは、Go言語のcmd/goツールにおけるWindows環境でのCgo利用時のリンカーに関する問題を修正するものです。具体的には、Cgoを介してC標準ライブラリ関数(特にfprintfのような関数)を使用する際に発生する__mingw_fprintfシンボルの欠落エラーに対処しています。この修正は、Windows環境でのCgoの安定性と互換性を向上させることを目的としています。

コミット

commit db71e1557b0f17921bbca101243f0fdec691d75c
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Thu Sep 19 01:20:02 2013 -0400

    cmd/go: fix missing __mingw_fprintf symbol for cgo on windows
    
    Fixes #5986.
    
    R=golang-dev, rsc, alex.brainman
    CC=golang-dev
    https://golang.org/cl/13261055

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

https://github.com/golang/go/commit/db71e1557b0f17921bbca101243f0fdec691d75c

元コミット内容

cmd/go: fix missing __mingw_fprintf symbol for cgo on windows

Fixes #5986.

R=golang-dev, rsc, alex.brainman
CC=golang-dev
https://golang.org/cl/13261055

変更の背景

このコミットは、Go Issue #5986「cmd/go: cgo on windows: missing __mingw_fprintf symbol」を修正するために行われました。この問題は、Windows環境でCgoを使用してCコードをコンパイルし、そのCコードがstdio.hに含まれるprintfなどの関数を使用する場合に発生していました。具体的には、リンカーが__mingw_fprintfというシンボルを見つけられずにエラーとなるというものでした。

Windows環境でGoのCgo機能を利用する際、GoプログラムはCコードとリンクされます。このリンクプロセスにおいて、MinGW(Minimalist GNU for Windows)環境で提供されるC標準ライブラリ(mingwexmingw32など)が使用されます。これらのライブラリ間には複雑な依存関係が存在し、特定の関数(この場合はfprintfの実装である__mingw_fprintf)が、リンカーが期待する順序で解決されない場合にシンボルが見つからないという問題が発生していました。

この問題は、特にprintfのような基本的なC標準ライブラリ関数を使用するCgoプログラムがWindowsでビルドできないという、GoとCgoの相互運用性における重要なバグでした。

前提知識の解説

Cgo

Cgoは、GoプログラムからC言語のコードを呼び出すためのGoの機能です。これにより、既存のCライブラリを利用したり、Goでは実装が難しい低レベルの操作を行ったりすることが可能になります。Cgoを使用すると、GoコンパイラはGoコードとCコードを連携させるための特別な処理を行い、最終的に両方のコードがリンクされた実行可能ファイルを生成します。

MinGW (Minimalist GNU for Windows)

MinGWは、Windows上でGCC(GNU Compiler Collection)などのGNU開発ツールチェーンを使用できるようにするツールセットです。Go言語がWindows上でCgoをサポートする際、内部的にMinGWのコンパイラやリンカーが利用されることがあります。MinGWは、Windows APIを直接呼び出すためのヘッダーファイルやライブラリを提供し、Windowsネイティブアプリケーションのビルドを可能にします。

__mingw_fprintf シンボル

__mingw_fprintfは、MinGW環境におけるfprintf関数の内部的な実装シンボルの一つです。C言語の標準入出力関数であるfprintfは、通常、stdio.hをインクルードすることで利用できますが、その具体的な実装はコンパイラやC標準ライブラリによって異なります。MinGWでは、fprintfのような関数がmingwexmingw32といったライブラリに分散して実装されており、これらのライブラリが相互に依存していることがあります。

リンカーとシンボル解決

リンカーは、コンパイルされたオブジェクトファイル(.oファイルなど)とライブラリを結合して、実行可能ファイルを生成するツールです。このプロセスにおいて、リンカーは各オブジェクトファイルやライブラリが参照するシンボル(関数名や変数名など)を、それらが定義されている場所(別のオブジェクトファイルやライブラリ)と結びつけます。これを「シンボル解決」と呼びます。

シンボル解決の際、リンカーは通常、指定されたライブラリを左から右へと順に検索し、未解決のシンボルを解決しようとします。もし、あるライブラリが別のライブラリに定義されているシンボルを参照している場合、その参照先のライブラリが参照元のライブラリよりもに指定されていないと、シンボルが見つからないエラーが発生することがあります。

リンカーグループ (-Wl,--start-group, -Wl,--end-group)

リンカーグループは、リンカーの挙動を制御するためのオプションです。特に、ライブラリ間に循環参照がある場合や、特定のシンボルが複数のライブラリに分散している場合に役立ちます。

  • -Wl,: これは、続くオプションがリンカー(ld)に渡されることをGCCに指示するプレフィックスです。
  • --start-group: このオプション以降に指定されるライブラリ群を「グループ」として扱います。リンカーは、このグループ内のライブラリを、グループ内のすべてのシンボルが解決されるまで、必要に応じて複数回検索します。
  • --end-group: リンカーグループの終わりを示します。

これにより、通常の左から右への一方向の検索では解決できないような、ライブラリ間の複雑な依存関係(特に循環依存)を解決することが可能になります。

技術的詳細

このコミットの技術的詳細の核心は、Windows環境におけるCgoのビルドプロセスで、MinGWのC標準ライブラリ(libmingwex.alibmingw32.a)のリンク順序と依存関係の解決方法を改善した点にあります。

元のsrc/cmd/go/build.goのコードでは、Windows向けにCgoがリンクする静的ライブラリとして、"-lmingwex""-lmingw32"が指定されていました。コメントには「libmingw32libmingwexlibgccも使うかもしれないので、libgccは最後に置くべき」とありましたが、これだけではmingwexmingw32間の相互依存関係を完全に解決できませんでした。

issue5986.goのテストケースが示すように、printfsqrtといった関数は、stdio.hmath.hを通じて利用されますが、これらの関数の具体的な実装はmingwexmingw32といったライブラリに分散しています。特にprintfは、内部的に__mingw_fprintfのようなシンボルに依存している可能性があり、このシンボルがmingwexmingw32のどちらか、あるいは両方にまたがって定義されている、または相互に参照している状況が考えられます。

リンカーが__mingw_fprintfシンボルを見つけられなかったのは、libmingwex.alibmingw32.aが通常のリンカーの検索順序(左から右への一方向検索)では、必要なシンボルをすべて解決できなかったためです。例えば、libmingwex.alibmingw32.a内のシンボルを参照し、同時にlibmingw32.alibmingwex.a内のシンボルを参照しているような循環依存がある場合、どちらかを先にリンクしても、もう一方のライブラリがまだ処理されていないためにシンボルが未解決のまま残ってしまいます。

この問題を解決するために、コミットではstaticLibsの定義を以下のように変更しました。

--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -2034,8 +2034,9 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string, gxxfile
 
 	var staticLibs []string
 	if goos == "windows" {
-		// libmingw32 and libmingwex might also use libgcc, so libgcc must come last
-		staticLibs = []string{"-lmingwex", "-lmingw32"}
+		// libmingw32 and libmingwex might also use libgcc, so libgcc must come last,
+		// and they also have some inter-dependencies, so must use linker groups.
+		staticLibs = []string{"-Wl,--start-group", "-lmingwex", "-lmingw32", "-Wl,--end-group"}
 	}
 	if cgoLibGccFile != "" {
 		staticLibs = append(staticLibs, cgoLibGccFile)

変更点として、"-lmingwex""-lmingw32"のライブラリ指定が、"-Wl,--start-group""-Wl,--end-group"で囲まれています。これにより、リンカーはmingwexmingw32を一つのグループとして扱い、このグループ内のすべてのシンボルが解決されるまで、グループ内のライブラリを繰り返し検索するようになります。この「繰り返し検索」のメカニズムが、ライブラリ間の循環依存や複雑な相互依存関係を効果的に解決し、結果として__mingw_fprintfのようなシンボルが正しく見つかるようになりました。

この修正は、GoのビルドシステムがWindows上でCgoを使用する際の堅牢性を高め、MinGW環境のリンカーの特性に合わせた適切なライブラリリンク戦略を適用したことを示しています。

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

このコミットにおけるコアとなるコードの変更は、以下の3つのファイルにわたります。

  1. misc/cgo/test/cgo_test.go:

    • func Test5986(t *testing.T) { test5986(t) } という新しいテスト関数が追加されました。これは、修正が正しく適用されたことを検証するためのものです。
  2. misc/cgo/test/issue5986.go:

    • このファイルは新規作成されました。Goのcgotestパッケージの一部として、Cgoを介してCのstdio.hmath.hを使用するCコードを含んでいます。
    • 特に、printf関数とsqrt関数を使用するoutput5986というC関数が定義されており、これがGo側から呼び出されます。このテストは、printfが内部的に依存する__mingw_fprintfシンボルが正しく解決されることを確認するために設計されています。
  3. src/cmd/go/build.go:

    • builder構造体のcgoメソッド内で、Windows (goos == "windows") の場合の静的ライブラリ (staticLibs) の指定方法が変更されました。
    • 変更前: staticLibs = []string{"-lmingwex", "-lmingw32"}
    • 変更後: staticLibs = []string{"-Wl,--start-group", "-lmingwex", "-lmingw32", "-Wl,--end-group"}
    • この変更により、mingwexmingw32ライブラリがリンカーグループとして扱われ、それらの間の相互依存関係が正しく解決されるようになります。

コアとなるコードの解説

misc/cgo/test/issue5986.go

// Copyright 2013 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package cgotest

/*
#cgo LDFLAGS: -lm
#include <stdio.h>
#include <math.h>

static void output5986()
{
    int current_row = 0, row_count = 0;
    double sum_squares = 0;
    do {
        if (current_row == 10) {
            current_row = 0;
        }
        ++row_count;
    }
    while (current_row++ != 1);
    double d =  sqrt(sum_squares / row_count);
    printf("sqrt is: %g\n", d);
}
*/
import "C"
import "testing"

func test5986(t *testing.T) {
	C.output5986()
}

このテストファイルは、問題の再現と修正の検証のために作成されました。

  • #cgo LDFLAGS: -lm: Cgoに、Cコードをリンクする際に数学ライブラリ(libm)もリンクするよう指示しています。sqrt関数を使用するために必要です。
  • #include <stdio.h>: C標準入出力ライブラリをインクルードしています。printf関数を使用するために必要です。
  • #include <math.h>: C数学ライブラリをインクルードしています。sqrt関数を使用するために必要です。
  • static void output5986(): このC関数は、printfsqrtという、MinGW環境でリンカーの問題を引き起こす可能性のある関数を使用しています。特にprintfは、__mingw_fprintfシンボルに依存していると推測されます。
  • import "C": GoからCコードを呼び出すためのCgoの構文です。
  • func test5986(t *testing.T) { C.output5986() }: Goのテスト関数で、定義されたC関数output5986を呼び出しています。この呼び出しが成功し、リンカーエラーが発生しないことが、修正の検証点となります。

src/cmd/go/build.go

diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go
index 6f35a87f1e..07d8f9ddc4 100644
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -2034,8 +2034,9 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string, gxxfile
 
 	var staticLibs []string
 	if goos == "windows" {
-		// libmingw32 and libmingwex might also use libgcc, so libgcc must come last
-		staticLibs = []string{"-lmingwex", "-lmingw32"}
+		// libmingw32 and libmingwex might also use libgcc, so libgcc must come last,
+		// and they also have some inter-dependencies, so must use linker groups.
+		staticLibs = []string{"-Wl,--start-group", "-lmingwex", "-lmingw32", "-Wl,--end-group"}
 	}
 	if cgoLibGccFile != "" {
 		staticLibs = append(staticLibs, cgoLibGccFile)

この変更は、Goのビルドツール(cmd/go)がCgoプログラムをコンパイルする際のリンカーオプションを調整しています。

  • if goos == "windows": このコードブロックは、ターゲットOSがWindowsの場合にのみ適用されます。
  • staticLibs = []string{"-Wl,--start-group", "-lmingwex", "-lmingw32", "-Wl,--end-group"}:
    • -Wl,: これは、Goのビルドツールが内部的に呼び出すGCC(またはMinGWのgcc)に対して、続くオプションをリンカー(ld)に直接渡すように指示するものです。
    • --start-group--end-group: これらはリンカーのオプションであり、libmingwex.alibmingw32.aをグループ化します。このグループ内のライブラリは、リンカーがすべてのシンボルを解決できるまで、必要に応じて複数回スキャンされます。これにより、mingwexmingw32間の循環依存や複雑な相互依存関係が解決され、__mingw_fprintfのようなシンボルが正しく見つかるようになります。
    • -lmingwex-lmingw32: それぞれlibmingwex.alibmingw32.aというライブラリをリンクすることを意味します。これらはMinGW環境でC標準ライブラリの機能を提供するものです。

この修正により、Windows環境でCgoを使用する際に、printfなどのC標準ライブラリ関数が正しくリンクされ、__mingw_fprintfシンボルが見つからないというエラーが解消されました。

関連リンク

参考にした情報源リンク