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

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

このコミットは、Go言語のビルドシステムにおける特定のバグ修正に関するものです。具体的には、64ビットWindowsシステム上で32ビットWindows向けGoプログラムをビルドする際に発生していた問題に対処しています。変更はinclude/libc.hファイル内の条件付きコンパイルディレクティブにあり、timespec構造体の定義に関するロジックが修正されています。

コミット

commit 0f1b4880935a4eddef135d4d32d2240196184e9f
Author: Russ Cox <rsc@golang.org>
Date:   Fri Mar 15 12:30:14 2013 -0400

    build: fix for 32-bit windows builds on 64-bit windows system
    
    Thanks to jon.forums@ for the fix.
    
    Fixes #5051.
    
    R=golang-dev, minux.ma
    CC=golang-dev
    https://golang.org/cl/7813045

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

https://github.com/golang/go/commit/0f1b4880935a4eddef135d4d32d2240196184e9f

元コミット内容

このコミットは、Go言語のビルドプロセスにおいて、64ビットWindows環境で32ビットWindowsバイナリをクロスコンパイルする際の不具合を修正します。具体的には、include/libc.hファイル内でtimespec構造体が誤って定義される問題を解決しています。この修正は、jon.forums@氏によって提供されたもので、Go issue #5051を解決します。

変更の背景

Go言語はクロスコンパイルを強力にサポートしており、異なるアーキテクチャやオペレーティングシステム向けのバイナリを生成できます。しかし、特定の環境の組み合わせ、特に64ビットWindows上で32ビットWindows向けのGoプログラムをビルドする際に、ビルドシステムが予期せぬ動作をしていました。

この問題は、Goのランタイムや標準ライブラリがC言語のコード(またはC言語のヘッダファイル)と連携する部分で発生していました。include/libc.hは、Goの内部で利用されるC言語の標準ライブラリ関数や構造体の定義を含むヘッダファイルであり、Goプログラムが低レベルのシステムコールやOS固有の機能とやり取りする際に重要です。

具体的な問題は、timespec構造体の定義に関するものでした。timespecは、秒とナノ秒で時間を表現するための構造体で、POSIX標準で定義されており、多くのUNIX系システムや、Windows上でもMinGWなどの環境で利用されます。Goのビルドシステムが、64ビットWindows上で32ビットターゲットをビルドする際に、このtimespec構造体が正しく条件付きコンパイルされず、結果としてビルドエラーや予期せぬ動作を引き起こしていました。

このバグはGo issue #5051として報告されており、このコミットはその報告された問題を解決するために行われました。

前提知識の解説

このコミットを理解するためには、以下の前提知識が役立ちます。

  • クロスコンパイル: あるプラットフォーム(ホスト)で、別のプラットフォーム(ターゲット)向けの実行可能ファイルを生成するプロセス。Go言語は、GOOS(ターゲットOS)やGOARCH(ターゲットアーキテクチャ)といった環境変数を設定することで、簡単にクロスコンパイルが可能です。
  • 条件付きコンパイル: C/C++言語において、プリプロセッサディレクティブ(#ifdef, #ifndef, #if, #elif, #else, #endifなど)を使用して、特定の条件が満たされた場合にのみコードの一部をコンパイルする仕組み。これにより、異なるプラットフォームやコンパイラ設定に応じて、適切なコードパスを選択できます。
  • _WIN32: Microsoft Visual C++ (MSVC) やMinGWなどのWindows向けコンパイラで定義されるプリプロセッサマクロ。Windowsプラットフォーム向けにコンパイルされていることを示します。
  • _WIN64: MSVCやMinGWなどのWindows向けコンパイラで定義されるプリプロセッサマクロ。64ビットWindowsプラットフォーム向けにコンパイルされていることを示します。
  • __MINGW64_VERSION_MAJOR: MinGW-w64コンパイラを使用している場合に定義されるプリプロセッサマクロ。MinGW-w64は、Windows上でGCCツールチェーンを提供するプロジェクトであり、32ビットおよび64ビットのWindowsバイナリを生成できます。このマクロは、MinGW-w64のメジャーバージョンを示します。
  • timespec構造体: POSIX標準で定義されている時間構造体。秒(tv_sec)とナノ秒(tv_nsec)で構成され、高精度な時間表現やタイマー、スリープ関数などで使用されます。Windows環境では、通常はtimeval構造体が使われることが多いですが、MinGWなどのPOSIX互換レイヤーではtimespecが提供されることがあります。
  • Goのビルドシステム: Go言語のビルドシステムは、Goソースコードをコンパイルして実行可能ファイルを生成するだけでなく、C言語で書かれた部分(Goランタイムの一部やcgoでリンクされるライブラリなど)も適切にコンパイル・リンクする役割を担っています。

技術的詳細

このコミットの技術的な核心は、include/libc.hファイル内のtimespec構造体の条件付き定義にあります。

元のコードでは、_WIN32が定義されている(つまりWindows向けビルドである)場合に、さらに_WIN64が定義されていない(つまり32ビットWindows向けビルドである)場合にのみtimespec構造体を定義していました。

#ifdef _WIN32

#ifndef _WIN64 // <-- ここが問題
struct timespec {
    int tv_sec;
    long tv_nsec;
};
#endif

// ...

このロジックは、Visual C++などのMicrosoftのコンパイラを使用する場合には適切に機能する可能性があります。しかし、MinGW-w64のようなGCCベースのクロスコンパイラを使用する場合に問題が発生しました。

MinGW-w64は、64ビットWindows上で動作しながら、32ビットWindowsターゲット向けのバイナリを生成できます。この場合、コンパイル環境は64ビットWindowsですが、ターゲットは32ビットWindowsです。MinGW-w64環境では、_WIN64マクロは定義されない可能性があります(ターゲットが32ビットであるため)。しかし、MinGW-w64自体がtimespec構造体を独自に提供している場合があり、Goのビルドシステムがこの構造体を再定義しようとすると、コンパイラエラーやリンカエラーが発生する可能性がありました。

修正後のコードでは、_WIN64が定義されていないことに加えて、__MINGW64_VERSION_MAJORも定義されていない場合にのみtimespec構造体を定義するように変更されました。

#ifdef _WIN32

#if !defined(_WIN64) && !defined(__MINGW64_VERSION_MAJOR) // <-- 修正箇所
struct timespec {
    int tv_sec;
    long tv_nsec;
};
#endif

// ...

この変更により、MinGW-w64コンパイラを使用している場合(__MINGW64_VERSION_MAJORが定義されている場合)は、たとえ_WIN64が定義されていなくても、Goのビルドシステムはtimespec構造体を再定義しなくなります。これは、MinGW-w64が既に適切なtimespec定義を提供していることを前提としています。これにより、コンパイラの衝突が回避され、64ビットWindows上での32ビットWindows向けGoビルドが正常に完了するようになります。

この修正は、Goのビルドシステムが異なるコンパイラツールチェーン(MSVCとMinGW)の挙動の違いを適切に考慮し、より堅牢なクロスコンパイル環境を提供する上で重要です。

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

変更はinclude/libc.hファイルにあります。

--- a/include/libc.h
+++ b/include/libc.h
@@ -308,7 +308,7 @@ extern	void	flagprint(int);\
 
 #ifdef _WIN32
 
-#ifndef _WIN64
+#if !defined(_WIN64) && !defined(__MINGW64_VERSION_MAJOR)
 struct timespec {
  \tint tv_sec;
  \tlong tv_nsec;

コアとなるコードの解説

変更された行は以下の通りです。

  • - #ifndef _WIN64 (変更前)
  • + #if !defined(_WIN64) && !defined(__MINGW64_VERSION_MAJOR) (変更後)

この変更は、timespec構造体を定義する条件をより厳密にしています。

  • 変更前: _WIN32が定義されており、かつ_WIN64が定義されていない場合にtimespecを定義していました。これは、32ビットWindows環境を意図していました。
  • 変更後: _WIN32が定義されており、かつ_WIN64が定義されておらず、さらに__MINGW64_VERSION_MAJORも定義されていない場合にのみtimespecを定義するように変更されました。

この追加された条件!defined(__MINGW64_VERSION_MAJOR)が重要です。 MinGW-w64コンパイラは、64ビットWindows上で32ビットターゲットをビルドする際に、_WIN64が定義されないことがあります。しかし、MinGW-w64自体はtimespec構造体を標準で提供しているため、Goのビルドシステムが独自にtimespecを定義しようとすると、重複定義となりコンパイルエラーを引き起こします。

この修正により、MinGW-w64を使用している場合(__MINGW64_VERSION_MAJORが定義されている場合)は、Goのビルドシステムはtimespecの定義をスキップし、MinGW-w64が提供する定義を利用するようになります。これにより、コンパイラ間の互換性の問題が解決され、64ビットWindows上での32ビットGoバイナリのビルドが成功するようになります。

関連リンク

参考にした情報源リンク