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

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

このコミットは、Go言語のcmd/cgoツールに関するビルド修正です。具体的には、以前の変更リスト(CL)で欠落していたファイルを追加することで、ビルドエラーを解消することを目的としています。追加されたファイルは、C.malloc(0)の挙動に関するテストケースであり、cgoとC言語のメモリ割り当て関数の連携における特定のシナリオを検証します。

コミット

commit 92dfbd3611bde7432ea7a58f17e248b8fa7224e0
Author: Russ Cox <rsc@golang.org>
Date:   Mon Sep 16 14:21:54 2013 -0400

    cmd/cgo: fix build (missing file from earlier CL)
    
    TBR=golang-dev
    CC=golang-dev
    https://golang.org/cl/13700045

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

https://github.com/golang/go/commit/92dfbd3611bde7432ea7a58f17e248b8fa7224e0

元コミット内容

このコミットは、以前の変更リスト(CL: Change List)で導入された変更によって発生したビルドエラーを修正するためのものです。具体的には、そのCLで追加されるべきだったテストファイル misc/cgo/test/issue6390.go が、何らかの理由でコミットに含まれていなかったため、ビルドが失敗していました。このコミットは、その欠落したファイルを追加することで、ビルドを正常な状態に戻します。

変更の背景

このコミットの背景には、Go言語のcgo機能におけるC.malloc(0)の挙動に関する問題、具体的にはGo issue 6390が存在します。

Go言語のcgoは、GoプログラムからC言語のコードを呼び出すためのメカニズムを提供します。C言語の標準ライブラリ関数であるmallocは、指定されたサイズのメモリをヒープから割り当て、そのメモリへのポインタを返します。通常、malloc(0)の挙動はC標準では未定義(implementation-defined)とされており、実装によってNULLを返すか、あるいはサイズ0の有効なポインタ(後でfreeできるポインタ)を返すかのどちらかになります。

Go言語のcgoは、C言語の関数をGoの型システムにマッピングする際に、この未定義の挙動をどのように扱うかという課題に直面します。特に、C.malloc(0)NULLを返した場合、Go側でそれをどのように扱うべきか、また、C.freeで解放できるポインタを返した場合に、Go側でそのポインタを適切に解放できるかどうかが問題となります。

Go issue 6390では、C.malloc(0)NULLを返す環境において、GoのcgoコードがNULLポインタを適切に処理できない可能性が指摘されました。このコミットで追加されるテストケースは、この特定のシナリオ、すなわちC.malloc(0)NULLを返した場合と、有効なポインタを返した場合の両方で、Goのcgoが正しく動作することを確認するために作成されました。

したがって、このコミットは、単なるビルド修正以上の意味を持ち、GoのcgoがC言語のmalloc(0)の挙動の多様性に対応し、堅牢性を高めるための重要なステップの一部と言えます。

前提知識の解説

cgo

cgoは、Go言語のプログラムからC言語の関数を呼び出したり、C言語のプログラムからGo言語の関数を呼び出したりするためのGoのツールです。Goのソースファイル内にC言語のコードを直接記述し、import "C"という特別なインポート宣言を使用することで、GoとCの間の相互運用を可能にします。

cgoを使用する主な理由は以下の通りです。

  • 既存のCライブラリの利用: 多くの高性能なライブラリやシステムレベルの機能はC言語で書かれています。cgoを使うことで、これらの既存の資産をGoプロジェクトで再利用できます。
  • パフォーマンスが重要な処理: 特定の計算集約的な処理やハードウェアに近い操作において、C言語のパフォーマンスが必要な場合にcgoが利用されます。
  • OS固有の機能へのアクセス: Goの標準ライブラリで提供されていないOS固有のAPIにアクセスするためにcgoが使われることがあります。

cgoの仕組みは、Goのビルドプロセス中にCコードをコンパイルし、Goコードとリンクすることで実現されます。これにより、Goの関数からCの関数を直接呼び出すことが可能になります。

malloc(0)の挙動

C言語の標準ライブラリ関数mallocは、指定されたサイズのメモリブロックを割り当てます。そのプロトタイプは以下の通りです。

void *malloc(size_t size);

ここで、sizeは割り当てるバイト数を指定します。

malloc(0)、つまりサイズ0のメモリを要求した場合の挙動は、C標準(ISO/IEC 9899:1999, C99)によって「実装定義(implementation-defined)」とされています。これは、コンパイラやオペレーティングシステムの実装によって挙動が異なる可能性があることを意味します。

一般的なmalloc(0)の挙動は以下のいずれかです。

  1. NULLポインタを返す: メモリを割り当てることができない、または割り当てる必要がないと判断し、NULLを返します。これはエラーを示す場合と同じ挙動です。
  2. サイズ0の有効なポインタを返す: 実際にメモリを割り当てないか、非常に小さい(例えば1バイト)メモリを割り当てて、そのポインタを返します。このポインタはNULLではなく、後でfree関数で解放できる必要があります。

この実装定義の性質が、Goのcgoのような異なる言語間のインターフェースにおいて問題を引き起こす可能性があります。GoのコードがC.malloc(0)を呼び出す際、返される値がNULLであるか、有効なポインタであるかによって、Go側での処理ロジックを調整する必要があるためです。

free関数

free関数は、malloccallocreallocなどのメモリ割り当て関数によって以前に割り当てられたメモリブロックを解放するために使用されます。

void free(void *ptr);

ptrは、解放するメモリブロックへのポインタです。ptrNULLの場合、free関数は何もしません。これは、malloc(0)NULLを返す実装の場合でも、安全にfree(NULL)を呼び出せることを意味します。

free関数は、解放されたメモリをシステムに返し、そのメモリを再利用可能にします。解放されたメモリにアクセスしようとすると、未定義の挙動(セグメンテーション違反など)を引き起こす可能性があります。

技術的詳細

このコミットで追加されたテストケースissue6390.goは、GoのcgoがC言語のmalloc(0)の挙動に適切に対応できることを検証します。

テスト関数test6390は、以下の2つのシナリオをテストしています。

  1. C.malloc(1024)のテスト:

    • p1 := C.malloc(1024): 1024バイトのメモリを割り当てます。
    • if p1 == nil: 割り当てが失敗しNULLが返された場合、テストを失敗させます。これは一般的なmallocの失敗ケースをカバーします。
    • C.free(p1): 割り当てられたメモリを解放します。
  2. C.malloc(0)のテスト:

    • p2 := C.malloc(0): 0バイトのメモリを割り当てます。
    • if p2 == nil: ここが重要なポイントです。malloc(0)NULLを返す実装の場合、p2nilになります。このテストは、p2nilであっても、Go側でそれを適切に処理できることを確認します。もしp2nilで、かつGoがnilポインタをC.freeに渡す際に問題を起こすようなら、このテストは失敗するでしょう。
    • C.free(p2): malloc(0)によって返されたポインタ(NULLであるか、有効なポインタであるかに関わらず)を解放します。C標準ではfree(NULL)は安全に何もしないことが保証されているため、p2nilであってもこの呼び出しは安全です。

このテストの目的は、Goのcgoが、C言語のmalloc(0)NULLを返す実装と、有効なポインタを返す実装の両方で、正しくメモリを割り当て、解放できることを保証することです。これにより、異なるプラットフォームやCコンパイラ環境におけるcgoの堅牢性が向上します。

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

このコミットによって追加されたファイルは以下の通りです。

misc/cgo/test/issue6390.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

// #include <stdlib.h>
import "C"

import "testing"

func test6390(t *testing.T) {
	p1 := C.malloc(1024)
	if p1 == nil {
		t.Fatalf("C.malloc(1024) returned nil")
	}
	p2 := C.malloc(0)
	if p2 == nil {
		t.Fatalf("C.malloc(0) returned nil")
	}
	C.free(p1)
	C.free(p2)
}

コアとなるコードの解説

追加されたissue6390.goファイルは、Goの標準テストパッケージtestingを使用したテストファイルです。

  • package cgotest: このファイルがcgotestパッケージの一部であることを示します。これはGoのcgoテストスイートの一部として実行されることを意味します。
  • // #include <stdlib.h>: これはcgoの特殊なコメント構文で、C言語の標準ライブラリヘッダstdlib.hをインクルードすることをGoコンパイラに指示します。stdlib.hにはmallocfreeといったメモリ管理関数が宣言されています。
  • import "C": これはcgoを使用するための特別なインポート宣言です。これにより、GoコードからC言語の関数や型にC.プレフィックスを付けてアクセスできるようになります。
  • import "testing": Goのテストフレームワークをインポートします。
  • func test6390(t *testing.T): Goのテスト関数です。t *testing.Tはテストの状態と結果を報告するためのオブジェクトです。
  • p1 := C.malloc(1024): C言語のmalloc関数を呼び出して1024バイトのメモリを割り当て、その結果をGoのポインタ型unsafe.Pointerに相当するp1に格納します。
  • if p1 == nil { t.Fatalf("C.malloc(1024) returned nil") }: mallocがメモリ割り当てに失敗した場合(NULLを返した場合)にテストを失敗させます。
  • p2 := C.malloc(0): C言語のmalloc関数を呼び出して0バイトのメモリを割り当て、その結果をp2に格納します。
  • if p2 == nil { t.Fatalf("C.malloc(0) returned nil") }: この行がこのテストの核心です。 前述の通り、malloc(0)NULLを返す実装と、有効なポインタを返す実装の両方があり得ます。このif文は、malloc(0)NULLを返した場合にテストを失敗させるように見えますが、実際にはそうではありません。Go issue 6390の文脈では、C.malloc(0)NULLを返した場合に、Go側でそのnilポインタをC.freeに渡しても問題なく処理できることを確認することが目的です。このt.Fatalfは、C.malloc(0)nilを返した場合に、それが予期せぬエラーとして扱われるべきではないことを示唆しています。つまり、C.malloc(0)nilを返しても、それは有効な結果であり、その後のC.free(p2)が安全に実行できることを期待しています。
  • C.free(p1): 割り当てられた1024バイトのメモリを解放します。
  • C.free(p2): 0バイトの割り当てによって返されたポインタ(NULLまたは有効なポインタ)を解放します。free(NULL)は安全であるため、p2nilであっても問題ありません。

このテストは、GoのcgoがC言語のmalloc(0)の挙動の多様性(NULLを返すか、有効なポインタを返すか)を適切に処理し、メモリリークやクラッシュを引き起こさないことを保証するためのものです。

関連リンク

参考にした情報源リンク

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

このコミットは、Go言語のcmd/cgoツールに関するビルド修正です。具体的には、以前の変更リスト(CL)で欠落していたファイルを追加することで、ビルドエラーを解消することを目的としています。追加されたファイルは、C.malloc(0)の挙動に関するテストケースであり、cgoとC言語のメモリ割り当て関数の連携における特定のシナリオを検証します。

コミット

commit 92dfbd3611bde7432ea7a58f17e248b8fa7224e0
Author: Russ Cox <rsc@golang.org>
Date:   Mon Sep 16 14:21:54 2013 -0400

    cmd/cgo: fix build (missing file from earlier CL)
    
    TBR=golang-dev
    CC=golang-dev
    https://golang.org/cl/13700045

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

https://github.com/golang/go/commit/92dfbd3611bde7432ea7a58f17e248b8fa7224e0

元コミット内容

このコミットは、以前の変更リスト(CL: Change List)で導入された変更によって発生したビルドエラーを修正するためのものです。具体的には、そのCLで追加されるべきだったテストファイル misc/cgo/test/issue6390.go が、何らかの理由でコミットに含まれていなかったため、ビルドが失敗していました。このコミットは、その欠落したファイルを追加することで、ビルドを正常な状態に戻します。

変更の背景

このコミットの背景には、Go言語のcgo機能におけるC.malloc(0)の挙動に関する問題、具体的にはGo issue 6390が存在します。

Go言語のcgoは、GoプログラムからC言語のコードを呼び出すためのメカニズムを提供します。C言語の標準ライブラリ関数であるmallocは、指定されたサイズのメモリをヒープから割り当て、そのメモリへのポインタを返します。通常、C標準においてmalloc(0)の挙動は未定義(implementation-defined)とされており、実装によってNULLを返すか、あるいはサイズ0の有効なポインタ(後でfreeできるポインタ)を返すかのどちらかになります。

しかし、GoのcgoにおけるC.malloc(0)は、このC標準の挙動とは異なり、常に非nilのポインタを返すように設計されています。これは、GoのcgoがCライブラリのmallocをラップするヘルパー関数を使用しており、このヘルパー関数がnilポインタを返さないことを保証しているためです。もし基盤となるCのmallocがメモリ不足を示した場合、Goのヘルパー関数はプログラムをクラッシュさせます。これはGoが自身のメモリ不足状況を処理する方法と同様です。したがって、C.mallocnilポインタを返すという観点からは、失敗しないように設計されています。

Go issue 6390では、このGoのcgoにおけるC.malloc(0)の特定の挙動が、テストによって適切に検証されているかどうかが問題となりました。このコミットで追加されるテストケースは、C.malloc(0)が非nilポインタを返すというGoのcgoの設計が正しく機能していることを確認するために作成されました。

したがって、このコミットは、単なるビルド修正以上の意味を持ち、GoのcgoがC言語のmalloc(0)の挙動の多様性に対応し、堅牢性を高めるための重要なステップの一部と言えます。

前提知識の解説

cgo

cgoは、Go言語のプログラムからC言語の関数を呼び出したり、C言語のプログラムからGo言語の関数を呼び出したりするためのGoのツールです。Goのソースファイル内にC言語のコードを直接記述し、import "C"という特別なインポート宣言を使用することで、GoとCの間の相互運用を可能にします。

cgoを使用する主な理由は以下の通りです。

  • 既存のCライブラリの利用: 多くの高性能なライブラリやシステムレベルの機能はC言語で書かれています。cgoを使うことで、これらの既存の資産をGoプロジェクトで再利用できます。
  • パフォーマンスが重要な処理: 特定の計算集約的な処理やハードウェアに近い操作において、C言語のパフォーマンスが必要な場合にcgoが利用されます。
  • OS固有の機能へのアクセス: Goの標準ライブラリで提供されていないOS固有のAPIにアクセスするためにcgoが使われることがあります。

cgoの仕組みは、Goのビルドプロセス中にCコードをコンパイルし、Goコードとリンクすることで実現されます。これにより、Goの関数からCの関数を直接呼び出すことが可能になります。

malloc(0)の挙動

C言語の標準ライブラリ関数mallocは、指定されたサイズのメモリブロックを割り当てます。そのプロトタイプは以下の通りです。

void *malloc(size_t size);

ここで、sizeは割り当てるバイト数を指定します。

malloc(0)、つまりサイズ0のメモリを要求した場合の挙動は、C標準(ISO/IEC 9899:1999, C99)によって「実装定義(implementation-defined)」とされています。これは、コンパイラやオペレーティングシステムの実装によって挙動が異なる可能性があることを意味します。

一般的なmalloc(0)の挙動は以下のいずれかです。

  1. NULLポインタを返す: メモリを割り当てることができない、または割り当てる必要がないと判断し、NULLを返します。これはエラーを示す場合と同じ挙動です。
  2. サイズ0の有効なポインタを返す: 実際にメモリを割り当てないか、非常に小さい(例えば1バイト)メモリを割り当てて、そのポインタを返します。このポインタはNULLではなく、後でfree関数で解放できる必要があります。

GoのcgoにおけるC.malloc(0)の特別な挙動: Goのcgoでは、C.malloc(0)はC標準の「実装定義」とは異なり、常に非nilのポインタを返すように設計されています。これは、GoのcgoがCライブラリのmallocを直接呼び出すのではなく、それをラップするGoのヘルパー関数を使用しているためです。このヘルパー関数は、C.mallocnilポインタを返さないことを保証します。もし基盤となるCのmallocがメモリ不足を示した場合、Goのヘルパー関数はプログラムをクラッシュさせます。これはGoが自身のメモリ不足状況を処理する方法と同様です。したがって、GoのC.mallocは、nilポインタを返すという観点からは失敗しないように設計されています。

free関数

free関数は、malloccallocreallocなどのメモリ割り当て関数によって以前に割り当てられたメモリブロックを解放するために使用されます。

void free(void *ptr);

ptrは、解放するメモリブロックへのポインタです。ptrNULLの場合、free関数は何もしません。これは、malloc(0)NULLを返す実装の場合でも、安全にfree(NULL)を呼び出せることを意味します。

free関数は、解放されたメモリをシステムに返し、そのメモリを再利用可能にします。解放されたメモリにアクセスしようとすると、未定義の挙動(セグメンテーション違反など)を引き起こす可能性があります。

技術的詳細

このコミットで追加されたテストケースissue6390.goは、GoのcgoがC言語のmalloc(0)の挙動に適切に対応できることを検証します。

テスト関数test6390は、以下の2つのシナリオをテストしています。

  1. C.malloc(1024)のテスト:

    • p1 := C.malloc(1024): 1024バイトのメモリを割り当てます。
    • if p1 == nil: 割り当てが失敗しNULLが返された場合、テストを失敗させます。これは一般的なmallocの失敗ケースをカバーします。
    • C.free(p1): 割り当てられたメモリを解放します。
  2. C.malloc(0)のテスト:

    • p2 := C.malloc(0): 0バイトのメモリを割り当てます。
    • if p2 == nil { t.Fatalf("C.malloc(0) returned nil") }: この行がこのテストの核心です。 前述の通り、GoのcgoにおけるC.malloc(0)は常に非nilポインタを返すように設計されています。したがって、このif p2 == nilの条件が真になることは、Goのcgoの設計に反する異常な状況を示します。もしp2nilになった場合、それはGoのcgoのC.mallocの実装が期待通りに動作していないことを意味するため、テストを失敗させます。
    • C.free(p2): malloc(0)によって返されたポインタ(Goのcgoの設計上は非nil)を解放します。C標準ではfree(NULL)は安全に何もしないことが保証されていますが、GoのcgoのC.malloc(0)nilを返さないため、このC.free(p2)は常に有効なポインタに対する解放操作となります。

このテストの目的は、Goのcgoが、C.malloc(0)が常に非nilのポインタを返すというGoのcgoの設計が正しく機能していることを保証することです。これにより、異なるプラットフォームやCコンパイラ環境におけるcgoの堅牢性が向上します。

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

このコミットによって追加されたファイルは以下の通りです。

misc/cgo/test/issue6390.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

// #include <stdlib.h>
import "C"

import "testing"

func test6390(t *testing.T) {
	p1 := C.malloc(1024)
	if p1 == nil {
		t.Fatalf("C.malloc(1024) returned nil")
	}
	p2 := C.malloc(0)
	if p2 == nil {
		t.Fatalf("C.malloc(0) returned nil")
	}
	C.free(p1)
	C.free(p2)
}

コアとなるコードの解説

追加されたissue6390.goファイルは、Goの標準テストパッケージtestingを使用したテストファイルです。

  • package cgotest: このファイルがcgotestパッケージの一部であることを示します。これはGoのcgoテストスイートの一部として実行されることを意味します。
  • // #include <stdlib.h>: これはcgoの特殊なコメント構文で、C言語の標準ライブラリヘッダstdlib.hをインクルードすることをGoコンパイラに指示します。stdlib.hにはmallocfreeといったメモリ管理関数が宣言されています。
  • import "C": これはcgoを使用するための特別なインポート宣言です。これにより、GoコードからC言語の関数や型にC.プレフィックスを付けてアクセスできるようになります。
  • import "testing": Goのテストフレームワークをインポートします。
  • func test6390(t *testing.T): Goのテスト関数です。t *testing.Tはテストの状態と結果を報告するためのオブジェクトです。
  • p1 := C.malloc(1024): C言語のmalloc関数を呼び出して1024バイトのメモリを割り当て、その結果をGoのポインタ型unsafe.Pointerに相当するp1に格納します。
  • if p1 == nil { t.Fatalf("C.malloc(1024) returned nil") }: mallocがメモリ割り当てに失敗した場合(NULLを返した場合)にテストを失敗させます。
  • p2 := C.malloc(0): C言語のmalloc関数を呼び出して0バイトのメモリを割り当て、その結果をp2に格納します。
  • if p2 == nil { t.Fatalf("C.malloc(0) returned nil") }: この行がこのテストの核心です。 GoのcgoにおけるC.malloc(0)は、Goのヘルパー関数によってラップされており、常に非nilのポインタを返すように設計されています。したがって、もしp2nilになった場合、それはGoのcgoのC.mallocの実装が期待通りに動作していないことを意味するため、テストを失敗させます。このt.Fatalfは、C.malloc(0)nilを返すという予期せぬエラーが発生した場合に、テストを即座に終了させる役割を果たします。
  • C.free(p1): 割り当てられた1024バイトのメモリを解放します。
  • C.free(p2): 0バイトの割り当てによって返されたポインタ(Goのcgoの設計上は非nil)を解放します。

このテストは、GoのcgoがC.malloc(0)に対して常に非nilのポインタを返すというGoのcgoの設計が正しく機能していることを保証するためのものです。

関連リンク

参考にした情報源リンク