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

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

このコミットは、Go言語のビルドツールチェーンの一部である cmd/dist における、Linux/ARM環境でのビルド問題を修正するものです。具体的には、find() 関数の戻り値の解釈が誤っていたために発生していた、CGO (C-Go interoperability) の有効/無効の判定ミスを修正しています。

コミット

commit c086bc1d7d383d29721ce4a379683f0f4670a536
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Wed Mar 7 14:24:28 2012 +0800

    cmd/dist: fix build for Linux/ARM
            find() returns -1 when not found.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/5752068

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

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

元コミット内容

cmd/dist: fix build for Linux/ARM find() returns -1 when not found.

このコミットは、cmd/dist ツールがLinux/ARM環境でGoをビルドする際に発生していた問題を修正するものです。問題の原因は、内部で使用されている find() 関数が、探索対象が見つからなかった場合に -1 を返すという仕様を、コードが正しく扱っていなかったことにありました。

変更の背景

Go言語のビルドシステムは、様々なオペレーティングシステム (OS) とアーキテクチャ (Arch) の組み合わせに対応しています。特に、C言語との相互運用機能であるCGOを有効にするかどうかは、ビルドターゲットのOS/Archに依存します。

cmd/dist はGoのソースコードからGo自身をビルドするためのツールであり、ビルド環境のセットアップや、CGOの有効/無効の判定など、多くの重要な役割を担っています。

このコミット以前のコードでは、src/cmd/dist/build.c 内の cmdenv 関数において、特定のOS/Archの組み合わせ (goos/goarch) がCGOをサポートするリスト (okcgo) に含まれているかを find() 関数で確認していました。

問題は、find() 関数が探索対象を見つけられなかった場合に -1 を返すにもかかわらず、その戻り値を単純にブール値として評価していた点にありました。C言語では、0 以外の整数値は真 (true) と評価されます。したがって、find()-1 を返した場合でも、条件式 if(find(...)) は真と評価されてしまい、実際にはCGOがサポートされていないLinux/ARM環境で誤って CGO_ENABLED1 に設定されてしまうというバグがありました。

この誤った設定により、Linux/ARM環境でのGoのビルドが失敗するか、あるいはCGO関連の機能が正しく動作しないという問題が発生していました。このコミットは、この find() 関数の戻り値の解釈の誤りを修正し、Linux/ARMでのビルドを正常に行えるようにすることを目的としています。

前提知識の解説

Go言語のビルドシステム (cmd/dist)

Go言語は、そのコンパイラやツールチェーン自体もGoで書かれています。cmd/dist は、GoのソースコードからGoのコンパイラ、リンカ、標準ライブラリ、その他のツールをビルドするためのブートストラップツールです。これは、Goの自己ホスト型コンパイラが動作するために不可欠な部分であり、Goのバージョンアップやクロスコンパイルの際に重要な役割を果たします。

cmd/dist は、ビルドターゲットのOSやアーキテクチャに応じて、適切なビルドフラグや環境変数を設定します。これには、CGOの有効/無効の判定も含まれます。

CGO (C-Go interoperability)

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

CGOを使用するには、Goのビルド時にCコンパイラ(通常はGCCやClang)が必要であり、特定のOS/アーキテクチャの組み合わせでのみサポートされます。CGO_ENABLED 環境変数は、GoのビルドプロセスにおいてCGOを有効にするか無効にするかを制御します。CGO_ENABLED=1 であればCGOが有効になり、CGO_ENABLED=0 であれば無効になります。この変数の設定は、cmd/dist によって行われます。

find() 関数の一般的な挙動とC言語の真偽値評価

プログラミングにおいて、findsearch といった名前の関数は、コレクションや配列の中から特定の要素を探し、その位置(インデックス)やポインタを返すのが一般的です。要素が見つからなかった場合、これらの関数は通常、特別な値(例: -1NULL、または特定の例外)を返して、その事実を呼び出し元に伝えます。

C言語では、条件式における真偽値の評価は以下のように行われます。

  • 0 は偽 (false) と評価されます。
  • 0 以外のすべての整数値(正の数、負の数に関わらず)は真 (true) と評価されます。

この特性が、今回のバグの根本原因でした。find() 関数が -1 を返した場合、C言語の条件式 if(find(...)) では -10 ではないため真と評価されてしまい、意図しないコードパスが実行されていました。

Linux/ARMアーキテクチャ

ARM (Advanced RISC Machine) は、モバイルデバイスや組み込みシステムで広く使用されているCPUアーキテクチャです。Linuxは、ARMプロセッサを搭載した多くのデバイスで動作する一般的なオペレーティングシステムです。Go言語は、Linux/ARMを含む多くのOS/Archの組み合わせをサポートしており、クロスコンパイル機能によって、ある環境で別の環境向けのバイナリを生成することができます。

この問題が特にLinux/ARMで顕在化したのは、おそらくその環境でCGOがデフォルトで無効であるか、あるいは特定のCGO依存関係が利用できないため、find() 関数が -1 を返すケースが頻繁に発生したためと考えられます。

技術的詳細

この修正は、src/cmd/dist/build.c ファイル内の cmdenv 関数にあります。cmdenv 関数は、Goのビルドプロセスで使用される様々な環境変数を設定する役割を担っています。

問題の箇所は、CGO_ENABLED 環境変数を設定するロジックです。このロジックは、現在のビルドターゲットのOSとアーキテクチャの組み合わせ (goos/goarch) が、CGOが有効なOS/Archのリスト (okcgo) に含まれているかどうかを判定するために find() 関数を使用しています。

元のコードは以下のようになっていました。

if(find(bprintf(&b, "%s/%s", goos, goarch), okcgo, nelem(okcgo)))
    xprintf(format, "CGO_ENABLED", "1");
else
    xprintf(format, "CGO_ENABLED", "0");

ここで find() 関数は、第一引数で指定された文字列(例: "linux/arm")が、第二引数で指定された文字列配列 (okcgo) の中に存在するかどうかを検索します。第三引数は配列の要素数です。find() は、見つかった場合はその要素のインデックスを返し、見つからなかった場合は -1 を返します。

前述の通り、C言語では 0 以外の値は真と評価されるため、find()-1 を返した場合でも if(-1) は真となり、CGO_ENABLED1 に設定されてしまっていました。

このコミットでは、この条件式を以下のように変更しました。

if(find(bprintf(&b, "%s/%s", goos, goarch), okcgo, nelem(okcgo)) >= 0)
    xprintf(format, "CGO_ENABLED", "1");
else
    xprintf(format, "CGO_ENABLED", "0");

この変更により、find() 関数が返す値が 0 以上(つまり、有効なインデックスが見つかった場合)にのみ、条件式が真と評価されるようになりました。これにより、find()-1 を返した場合(見つからなかった場合)は条件式が偽となり、CGO_ENABLED が正しく 0 に設定されるようになります。

この修正は、C言語における真偽値の評価規則と、特定の関数の戻り値のセマンティクスを正確に理解することの重要性を示しています。

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

--- a/src/cmd/dist/build.c
+++ b/src/cmd/dist/build.c
@@ -1321,7 +1321,7 @@ cmdenv(int argc, char **argv)
  xprintf(format, "GOTOOLDIR", tooldir);
  xprintf(format, "GOCHAR", gochar);
 
- if(find(bprintf(&b, "%s/%s", goos, goarch), okcgo, nelem(okcgo)))
+ if(find(bprintf(&b, "%s/%s", goos, goarch), okcgo, nelem(okcgo)) >= 0)
  xprintf(format, "CGO_ENABLED", "1");
  else
  xprintf(format, "CGO_ENABLED", "0");

コアとなるコードの解説

変更は src/cmd/dist/build.c ファイルの1324行目(変更前)にあります。

元のコード: if(find(bprintf(&b, "%s/%s", goos, goarch), okcgo, nelem(okcgo)))

修正後のコード: if(find(bprintf(&b, "%s/%s", goos, goarch), okcgo, nelem(okcgo)) >= 0)

この変更の核心は、find() 関数の戻り値の解釈を修正した点です。

  • find(bprintf(&b, "%s/%s", goos, goarch), okcgo, nelem(okcgo)) の部分は、現在のOS (goos) とアーキテクチャ (goarch) の組み合わせ(例: "linux/arm")が、CGOが有効な組み合わせのリスト (okcgo) の中に存在するかどうかを検索しています。
  • find() 関数は、見つかった場合はその要素の配列インデックス(0以上の整数)を返し、見つからなかった場合は -1 を返します。
  • 元の if(...) の形式では、C言語のルールに従い、find()-1 を返した場合でも、-10 ではないため真と評価されていました。これにより、CGOがサポートされていない環境でも CGO_ENABLED1 に設定されてしまうという誤動作が発生していました。
  • 修正後の if(... >= 0) という条件式は、find() の戻り値が 0 以上である場合にのみ真と評価されるようにします。これは、find() が有効なインデックスを返した場合(つまり、CGOがサポートされている組み合わせが見つかった場合)にのみ CGO_ENABLED1 に設定するという、意図されたロジックを正確に反映しています。

このシンプルな変更により、GoのビルドシステムがCGOの有効/無効を正しく判定できるようになり、特にLinux/ARMのような特定の環境でのビルドの安定性が向上しました。

関連リンク

  • Go言語の公式ウェブサイト: https://go.dev/
  • Go言語のソースコードリポジトリ (GitHub): https://github.com/golang/go
  • Go言語のCGOに関するドキュメント: https://go.dev/blog/c-go-is-not-c (CGOの基本的な概念について)
  • Go言語のビルドシステムに関する情報 (Goのソースコード内): src/cmd/dist/ ディレクトリ内のドキュメントやコードが参考になります。

参考にした情報源リンク

  • GitHubのコミットページ: https://github.com/golang/go/commit/c086bc1d7d383d29721ce4a379683f0f4670a536
  • Go言語のソースコード (src/cmd/dist/build.c): コミットが適用された実際のコードベース。
  • C言語の真偽値評価に関する一般的な知識。
  • Go言語のビルドプロセスおよびCGOに関する一般的な知識。
  • Goのコードレビューシステム (Gerrit) のCL (Change-list) ページ: https://golang.org/cl/5752068 (コミットメッセージに記載されているリンク)I have generated the detailed explanation based on the commit information and the requested structure. I have included background, prerequisite knowledge, technical details, and code changes, all in Japanese. I have also provided relevant links. The output is ready to be printed to standard output.# [インデックス 12459] ファイルの概要

このコミットは、Go言語のビルドツールチェーンの一部である cmd/dist における、Linux/ARM環境でのビルド問題を修正するものです。具体的には、find() 関数の戻り値の解釈が誤っていたために発生していた、CGO (C-Go interoperability) の有効/無効の判定ミスを修正しています。

コミット

commit c086bc1d7d383d29721ce4a379683f0f4670a536
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Wed Mar 7 14:24:28 2012 +0800

    cmd/dist: fix build for Linux/ARM
            find() returns -1 when not found.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/5752068

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

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

元コミット内容

cmd/dist: fix build for Linux/ARM find() returns -1 when not found.

このコミットは、cmd/dist ツールがLinux/ARM環境でGoをビルドする際に発生していた問題を修正するものです。問題の原因は、内部で使用されている find() 関数が、探索対象が見つからなかった場合に -1 を返すという仕様を、コードが正しく扱っていなかったことにありました。

変更の背景

Go言語のビルドシステムは、様々なオペレーティングシステム (OS) とアーキテクチャ (Arch) の組み合わせに対応しています。特に、C言語との相互運用機能であるCGOを有効にするかどうかは、ビルドターゲットのOS/Archに依存します。

cmd/dist はGoのソースコードからGo自身をビルドするためのツールであり、ビルド環境のセットアップや、CGOの有効/無効の判定など、多くの重要な役割を担っています。

このコミット以前のコードでは、src/cmd/dist/build.c 内の cmdenv 関数において、特定のOS/Archの組み合わせ (goos/goarch) がCGOをサポートするリスト (okcgo) に含まれているかを find() 関数で確認していました。

問題は、find() 関数が探索対象を見つけられなかった場合に -1 を返すにもかかわらず、その戻り値を単純にブール値として評価していた点にありました。C言語では、0 以外の整数値は真 (true) と評価されます。したがって、find()-1 を返した場合でも、条件式 if(find(...)) は真と評価されてしまい、実際にはCGOがサポートされていないLinux/ARM環境で誤って CGO_ENABLED1 に設定されてしまうというバグがありました。

この誤った設定により、Linux/ARM環境でのGoのビルドが失敗するか、あるいはCGO関連の機能が正しく動作しないという問題が発生していました。このコミットは、この find() 関数の戻り値の解釈の誤りを修正し、Linux/ARMでのビルドを正常に行えるようにすることを目的としています。

前提知識の解説

Go言語のビルドシステム (cmd/dist)

Go言語は、そのコンパイラやツールチェーン自体もGoで書かれています。cmd/dist は、GoのソースコードからGoのコンパイラ、リンカ、標準ライブラリ、その他のツールをビルドするためのブートストラップツールです。これは、Goの自己ホスト型コンパイラが動作するために不可欠な部分であり、Goのバージョンアップやクロスコンパイルの際に重要な役割を果たします。

cmd/dist は、ビルドターゲットのOSやアーキテクチャに応じて、適切なビルドフラグや環境変数を設定します。これには、CGOの有効/無効の判定も含まれます。

CGO (C-Go interoperability)

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

CGOを使用するには、Goのビルド時にCコンパイラ(通常はGCCやClang)が必要であり、特定のOS/アーキテクチャの組み合わせでのみサポートされます。CGO_ENABLED 環境変数は、GoのビルドプロセスにおいてCGOを有効にするか無効にするかを制御します。CGO_ENABLED=1 であればCGOが有効になり、CGO_ENABLED=0 であれば無効になります。この変数の設定は、cmd/dist によって行われます。

find() 関数の一般的な挙動とC言語の真偽値評価

プログラミングにおいて、findsearch といった名前の関数は、コレクションや配列の中から特定の要素を探し、その位置(インデックス)やポインタを返すのが一般的です。要素が見つからなかった場合、これらの関数は通常、特別な値(例: -1NULL、または特定の例外)を返して、その事実を呼び出し元に伝えます。

C言語では、条件式における真偽値の評価は以下のように行われます。

  • 0 は偽 (false) と評価されます。
  • 0 以外のすべての整数値(正の数、負の数に関わらず)は真 (true) と評価されます。

この特性が、今回のバグの根本原因でした。find() 関数が -1 を返した場合、C言語の条件式 if(find(...)) では -10 ではないため真と評価されてしまい、意図しないコードパスが実行されていました。

Linux/ARMアーキテクチャ

ARM (Advanced RISC Machine) は、モバイルデバイスや組み込みシステムで広く使用されているCPUアーキテクチャです。Linuxは、ARMプロセッサを搭載した多くのデバイスで動作する一般的なオペレーティングシステムです。Go言語は、Linux/ARMを含む多くのOS/Archの組み合わせをサポートしており、クロスコンパイル機能によって、ある環境で別の環境向けのバイナリを生成することができます。

この問題が特にLinux/ARMで顕在化したのは、おそらくその環境でCGOがデフォルトで無効であるか、あるいは特定のCGO依存関係が利用できないため、find() 関数が -1 を返すケースが頻繁に発生したためと考えられます。

技術的詳細

この修正は、src/cmd/dist/build.c ファイル内の cmdenv 関数にあります。cmdenv 関数は、Goのビルドプロセスで使用される様々な環境変数を設定する役割を担っています。

問題の箇所は、CGO_ENABLED 環境変数を設定するロジックです。このロジックは、現在のビルドターゲットのOSとアーキテクチャの組み合わせ (goos/goarch) が、CGOが有効なOS/Archのリスト (okcgo) に含まれているかどうかを判定するために find() 関数を使用しています。

元のコードは以下のようになっていました。

if(find(bprintf(&b, "%s/%s", goos, goarch), okcgo, nelem(okcgo)))
    xprintf(format, "CGO_ENABLED", "1");
else
    xprintf(format, "CGO_ENABLED", "0");

ここで find() 関数は、第一引数で指定された文字列(例: "linux/arm")が、第二引数で指定された文字列配列 (okcgo) の中に存在するかどうかを検索します。第三引数は配列の要素数です。find() は、見つかった場合はその要素のインデックスを返し、見つからなかった場合は -1 を返します。

前述の通り、C言語では 0 以外の値は真と評価されるため、find()-1 を返した場合でも if(-1) は真となり、CGO_ENABLED1 に設定されてしまっていました。

このコミットでは、この条件式を以下のように変更しました。

if(find(bprintf(&b, "%s/%s", goos, goarch), okcgo, nelem(okcgo)) >= 0)
    xprintf(format, "CGO_ENABLED", "1");
else
    xprintf(format, "CGO_ENABLED", "0");

この変更により、find() 関数が返す値が 0 以上(つまり、有効なインデックスが見つかった場合)にのみ、条件式が真と評価されるようになりました。これにより、find()-1 を返した場合(見つからなかった場合)は条件式が偽となり、CGO_ENABLED が正しく 0 に設定されるようになります。

この修正は、C言語における真偽値の評価規則と、特定の関数の戻り値のセマンティクスを正確に理解することの重要性を示しています。

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

--- a/src/cmd/dist/build.c
+++ b/src/cmd/dist/build.c
@@ -1321,7 +1321,7 @@ cmdenv(int argc, char **argv)
  xprintf(format, "GOTOOLDIR", tooldir);
  xprintf(format, "GOCHAR", gochar);
 
- if(find(bprintf(&b, "%s/%s", goos, goarch), okcgo, nelem(okcgo)))
+ if(find(bprintf(&b, "%s/%s", goos, goarch), okcgo, nelem(okcgo)) >= 0)
  xprintf(format, "CGO_ENABLED", "1");
  else
  xprintf(format, "CGO_ENABLED", "0");

コアとなるコードの解説

変更は src/cmd/dist/build.c ファイルの1324行目(変更前)にあります。

元のコード: if(find(bprintf(&b, "%s/%s", goos, goarch), okcgo, nelem(okcgo)))

修正後のコード: if(find(bprintf(&b, "%s/%s", goos, goarch), okcgo, nelem(okcgo)) >= 0)

この変更の核心は、find() 関数の戻り値の解釈を修正した点です。

  • find(bprintf(&b, "%s/%s", goos, goarch), okcgo, nelem(okcgo)) の部分は、現在のOS (goos) とアーキテクチャ (goarch) の組み合わせ(例: "linux/arm")が、CGOが有効な組み合わせのリスト (okcgo) の中に存在するかどうかを検索しています。
  • find() 関数は、見つかった場合はその要素の配列インデックス(0以上の整数)を返し、見つからなかった場合は -1 を返します。
  • 元の if(...) の形式では、C言語のルールに従い、find()-1 を返した場合でも、-10 ではないため真と評価されていました。これにより、CGOがサポートされていない環境でも CGO_ENABLED1 に設定されてしまうという誤動作が発生していました。
  • 修正後の if(... >= 0) という条件式は、find() の戻り値が 0 以上である場合にのみ真と評価されるようにします。これは、find() が有効なインデックスを返した場合(つまり、CGOがサポートされている組み合わせが見つかった場合)にのみ CGO_ENABLED1 に設定するという、意図されたロジックを正確に反映しています。

このシンプルな変更により、GoのビルドシステムがCGOの有効/無効を正しく判定できるようになり、特にLinux/ARMのような特定の環境でのビルドの安定性が向上しました。

関連リンク

  • Go言語の公式ウェブサイト: https://go.dev/
  • Go言語のソースコードリポジトリ (GitHub): https://github.com/golang/go
  • Go言語のCGOに関するドキュメント: https://go.dev/blog/c-go-is-not-c (CGOの基本的な概念について)
  • Go言語のビルドシステムに関する情報 (Goのソースコード内): src/cmd/dist/ ディレクトリ内のドキュメントやコードが参考になります。

参考にした情報源リンク

  • GitHubのコミットページ: https://github.com/golang/go/commit/c086bc1d7d383d29721ce4a379683f0f4670a536
  • Go言語のソースコード (src/cmd/dist/build.c): コミットが適用された実際のコードベース。
  • C言語の真偽値評価に関する一般的な知識。
  • Go言語のビルドプロセスおよびCGOに関する一般的な知識。
  • Goのコードレビューシステム (Gerrit) のCL (Change-list) ページ: https://golang.org/cl/5752068 (コミットメッセージに記載されているリンク)