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

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

このコミットは、Go言語のnetパッケージにおけるCgo関連のコード変更です。具体的には、getaddrinfoシステムコールで使用されるアドレス情報フラグ(AI_*定数)の定義を、プラットフォーム固有のファイルに移動しています。これにより、NetBSDやOpenBSDのような、一部のAI_*定数が存在しないプラットフォームでのCgoの利用を可能にすることを目的としています。

コミット

  • コミットハッシュ: eb4138f48114de303d8844f4fa2ff872e2a7a678
  • 作者: Joel Sing jsing@google.com
  • 日付: Sun Jun 3 23:54:14 2012 +1000

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

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

元コミット内容

net: move cgo address info flags to per-platform files

Move address info flags to per-platform files. This is needed to
enable cgo on NetBSD (and later OpenBSD), as some of the currently
used AI_* defines do not exist on these platforms.

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/6250075

変更の背景

この変更の主な背景は、Go言語のnetパッケージがCgo(GoとC言語の相互運用機能)を使用してシステムコールを呼び出す際に、異なるオペレーティングシステム間での互換性の問題を解決することにあります。

特に、getaddrinfoというネットワークアドレス解決のためのシステムコールは、その挙動を制御するために様々なフラグ(AI_CANONNAME, AI_V4MAPPED, AI_ALL, AI_ADDRCONFIGなど)を受け取ります。これらのフラグは、POSIX標準で定義されていますが、すべてのUnix系OSで全く同じ定数が利用できるわけではありません。

コミットメッセージによると、NetBSD(そして将来的にはOpenBSD)では、Goが当時使用していた一部のAI_*定数が存在しないため、Cgoを介したgetaddrinfoの呼び出しが正しく機能しない問題がありました。この問題を解決するため、プラットフォームごとに利用可能なフラグを個別に定義するアプローチが採用されました。これにより、各OSの特性に合わせた適切なフラグセットを提供し、Cgoの互換性と移植性を向上させることが目的です。

また、Linux環境におけるAI_ADDRCONFIGフラグの特定の挙動に関する注意書きも含まれており、このフラグがLinux上でgetaddrinfoの返す正規名(canonical name)を誤らせる可能性があるため、意図的に除外されています。これは、単に定数が存在しないという問題だけでなく、定数のセマンティクスがプラットフォーム間で異なる場合にも対応する必要があることを示唆しています。

前提知識の解説

このコミットを理解するためには、以下の技術的な概念について理解しておく必要があります。

  1. Cgo:

    • Cgoは、GoプログラムからC言語のコードを呼び出すためのGoの機能です。Goのnetパッケージのような低レベルのネットワーク操作では、OSが提供するC言語のシステムコール(例: getaddrinfo)を直接呼び出す必要がある場合があり、その際にCgoが利用されます。
    • Cgoを使用すると、Goのコード内でCのヘッダーファイルをインポートし、Cの関数や構造体を利用できます。
  2. getaddrinfoシステムコール:

    • getaddrinfoは、ホスト名とサービス名(ポート番号など)から、ネットワークアドレス構造体(sockaddr構造体)のリストを取得するための標準的なC言語のAPIです。これは、アプリケーションがネットワーク接続を確立する際に、IPアドレスやポート番号を解決するために広く使用されます。
    • この関数は、hintsという構造体を引数として受け取り、その中に検索の挙動を制御するためのフラグ(ai_flags)を設定できます。
  3. AI_* フラグ: getaddrinfoai_flagsに設定される主要なフラグには以下のようなものがあります。

    • AI_CANONNAME: 解決されたホストの正規名(canonical name)をai_canonnameフィールドに設定するよう要求します。
    • AI_V4MAPPED: IPv6対応のソケットアドレス構造体で、IPv4アドレスをIPv6アドレスにマップして返すことを許可します。これにより、IPv6のみを扱うアプリケーションでもIPv4ホストに接続できるようになります。
    • AI_ALL: AI_V4MAPPEDと組み合わせて使用され、IPv6アドレスと、IPv4マップされたIPv6アドレスの両方を返します。
    • AI_ADDRCONFIG: ローカルシステムに設定されているIPアドレスの種類(IPv4またはIPv6)に基づいて、返されるアドレスの種類をフィルタリングします。例えば、IPv4アドレスが設定されていないシステムではIPv4アドレスを返さない、といった挙動になります。このフラグは、ネットワーク設定に依存した挙動をするため、注意が必要です。
  4. プラットフォーム固有のコード:

    • Go言語では、ビルドタグ(build tags)を使用して、特定のオペレーティングシステムやアーキテクチャに特化したコードを記述することができます。例えば、// +build linuxという行がファイルの先頭にある場合、そのファイルはLinuxシステムでのみコンパイルされます。
    • このコミットでは、cgo_bsd.gocgo_linux.gocgo_unix.goといったファイル名が使用されており、これらはそれぞれBSD系OS、Linux、一般的なUnix系OS向けのCgo関連コードを格納していることを示唆しています。これにより、OSごとの差異を吸収し、コードの移植性を高めています。

技術的詳細

このコミットの技術的な核心は、getaddrinfoシステムコールに渡すai_flagsの値を、プラットフォームごとに異なるGoの関数で提供するように変更した点です。

以前は、src/pkg/net/cgo_unix.goのような汎用的なUnix向けファイルで、すべてのAI_*フラグがハードコードされていました。しかし、これはNetBSDやOpenBSDのような一部のプラットフォームで問題を引き起こしました。これらのOSでは、Goが期待するAI_*定数の一部(例えばAI_ALLAI_V4MAPPEDなど、あるいはその組み合わせ)がCライブラリに存在しないか、異なる値を持つ可能性がありました。その結果、コンパイルエラーや予期せぬランタイムエラーが発生し、Cgoを利用したネットワーク機能が正しく動作しませんでした。

この問題を解決するため、コミットでは以下の変更が行われました。

  1. cgoAddrInfoMask() から cgoAddrInfoFlags() への変更:

    • 以前はcgoAddrInfoMask()という関数があり、これはAI_MASKという定数を返していました。このAI_MASKが具体的に何を意味していたかは不明ですが、おそらく利用可能なフラグのビットマスクとして機能していた可能性があります。
    • 新しいアプローチでは、cgoAddrInfoFlags()という関数が導入され、これはgetaddrinfoに直接渡すべきai_flagsの値を返します。これにより、各プラットフォームが独自のフラグセットを定義できるようになります。
  2. プラットフォーム固有のフラグ定義:

    • src/pkg/net/cgo_bsd.go (BSD系OS向け): C.AI_CANONNAME | C.AI_V4MAPPED | C.AI_ALL を返します。これは、BSD系OSでこれらのフラグが利用可能であることを前提としています。
    • src/pkg/net/cgo_linux.go (Linux向け): こちらも C.AI_CANONNAME | C.AI_V4MAPPED | C.AI_ALL を返しますが、重要なコメントが追加されています。このコメントは、AI_ADDRCONFIGフラグを意図的に含めない理由を説明しています。LinuxではAI_ADDRCONFIGを設定すると、getaddrinfoが誤った正規名を返す可能性があるため、このフラグは除外されています。これは、単に定数の有無だけでなく、そのセマンティクスがプラットフォーム間で異なる場合の対応例です。
    • src/pkg/net/cgo_unix.go (汎用Unix向け): このファイルからは、以前のハードコードされたフラグ定義が削除され、代わりにcgoAddrInfoFlags()を呼び出すように変更されました。これにより、具体的なフラグの選択はプラットフォーム固有のファイルに委譲されます。

この変更により、Goのnetパッケージは、各OSのCライブラリが提供するAI_*定数の差異を吸収し、より広範なプラットフォームでCgoベースのネットワーク機能が安定して動作するようになりました。特に、NetBSDやOpenBSDのような、より厳密なPOSIX準拠や独自のCライブラリ実装を持つシステムへの対応が強化されました。

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

このコミットでは、以下の3つのファイルが変更されています。

  1. src/pkg/net/cgo_bsd.go:

    • cgoAddrInfoMask() 関数が cgoAddrInfoFlags() に変更されました。
    • 返される値が C.AI_MASK から C.AI_CANONNAME | C.AI_V4MAPPED | C.AI_ALL に変更されました。
    --- a/src/pkg/net/cgo_bsd.go
    +++ b/src/pkg/net/cgo_bsd.go
    @@ -11,6 +11,6 @@ package net
     */
     import "C"
    
    -func cgoAddrInfoMask() C.int {
    -	return C.AI_MASK
    +func cgoAddrInfoFlags() C.int {
    +	return C.AI_CANONNAME | C.AI_V4MAPPED | C.AI_ALL
     }
    
  2. src/pkg/net/cgo_linux.go:

    • cgoAddrInfoMask() 関数が cgoAddrInfoFlags() に変更されました。
    • 返される値が C.AI_CANONNAME | C.AI_V4MAPPED | C.AI_ALL に変更されました。
    • AI_ADDRCONFIG を含めない理由に関するコメントが追加されました。
    --- a/src/pkg/net/cgo_linux.go
    +++ b/src/pkg/net/cgo_linux.go
    @@ -9,6 +9,12 @@ package net
     */
     import "C"
    
    -func cgoAddrInfoMask() C.int {
    +func cgoAddrInfoFlags() C.int {
    +	// NOTE(rsc): In theory there are approximately balanced
    +	// arguments for and against including AI_ADDRCONFIG
    +	// in the flags (it includes IPv4 results only on IPv4 systems,
    +	// and similarly for IPv6), but in practice setting it causes
    +	// getaddrinfo to return the wrong canonical name on Linux.
    +	// So definitely leave it out.
     	return C.AI_CANONNAME | C.AI_V4MAPPED | C.AI_ALL
     }
    
  3. src/pkg/net/cgo_unix.go:

    • cgoLookupIPCNAME 関数内の hints.ai_flags の設定箇所が変更されました。
    • 以前のハードコードされたフラグ定義 (C.AI_ALL | C.AI_V4MAPPED | C.AI_CANONNAME) & cgoAddrInfoMask() が削除され、代わりに cgoAddrInfoFlags() の呼び出しに置き換えられました。
    • AI_ADDRCONFIG に関するコメントがこのファイルから削除されました(cgo_linux.go に移動)。
    --- a/src/pkg/net/cgo_unix.go
    +++ b/src/pkg/net/cgo_unix.go
    @@ -81,13 +81,7 @@ func cgoLookupIPCNAME(name string) (addrs []IP, cname string, err error, complet
     	var res *C.struct_addrinfo
     	var hints C.struct_addrinfo
    
    -	// NOTE(rsc): In theory there are approximately balanced
    -	// arguments for and against including AI_ADDRCONFIG
    -	// in the flags (it includes IPv4 results only on IPv4 systems,
    -	// and similarly for IPv6), but in practice setting it causes
    -	// getaddrinfo to return the wrong canonical name on Linux.
    -	// So definitely leave it out.
    -	hints.ai_flags = (C.AI_ALL | C.AI_V4MAPPED | C.AI_CANONNAME) & cgoAddrInfoMask()
    +	hints.ai_flags = cgoAddrInfoFlags()
    
     	h := C.CString(name)
     	defer C.free(unsafe.Pointer(h))
    

コアとなるコードの解説

このコミットのコアとなる変更は、getaddrinfoシステムコールに渡すai_flagsの値を、プラットフォーム固有のGoファイルで定義されたcgoAddrInfoFlags()関数を通じて取得するようにした点です。

  • cgoAddrInfoFlags() 関数の導入:

    • この関数は、各プラットフォーム(BSD、Linuxなど)のCgo関連ファイルに定義されています。
    • その役割は、当該プラットフォームでgetaddrinfoに渡すべき適切なAI_*フラグのビットマスクをC.int型で返すことです。
    • これにより、プラットフォームごとに異なるAI_*定数の可用性や挙動の差異を吸収し、Goのnetパッケージがより多くのOSで正しく動作するようにします。
  • src/pkg/net/cgo_bsd.gosrc/pkg/net/cgo_linux.go の役割:

    • これらのファイルは、それぞれのOS向けに特化したcgoAddrInfoFlags()の実装を提供します。
    • 両者とも C.AI_CANONNAME | C.AI_V4MAPPED | C.AI_ALL を返していますが、これはこれらのフラグがGoのネットワーク解決ロジックにとって重要であり、かつこれらのプラットフォームで利用可能であることを示しています。
    • 特にcgo_linux.goには、AI_ADDRCONFIGフラグに関する重要なコメントがあります。このコメントは、Linux環境でAI_ADDRCONFIGを使用するとgetaddrinfoが誤った正規名を返す可能性があるため、意図的にこのフラグを含めていないことを明記しています。これは、単に定数が存在するかどうかだけでなく、その定数のセマンティクスがプラットフォーム間で異なる場合に、Goがどのように対応しているかを示す良い例です。
  • src/pkg/net/cgo_unix.go の変更:

    • このファイルは、汎用的なUnix系OS向けのCgoコードを含んでいます。
    • 以前は、cgoLookupIPCNAME関数内でhints.ai_flagsがハードコードされたフラグとcgoAddrInfoMask()の組み合わせで設定されていました。
    • 今回の変更で、このハードコードされたロジックが削除され、代わりにhints.ai_flags = cgoAddrInfoFlags()とシンプルに呼び出す形になりました。
    • これにより、cgo_unix.go自体は具体的なフラグの選択に関与せず、その役割をプラットフォーム固有のファイルに委譲することで、コードのモジュール性と移植性が向上しています。

この変更は、Go言語が異なるOS環境で一貫したネットワーク機能を提供するために、Cgoを介したシステムコール呼び出しの細部にまで注意を払っていることを示しています。

関連リンク

参考にした情報源リンク

  • Go言語のソースコード(GitHubリポジトリ): https://github.com/golang/go
  • Go言語のコードレビューシステム(Gerrit): https://go.dev/cl/6250075 (コミットメッセージに記載されているCLリンク)
  • AI_ADDRCONFIGに関する議論や情報(例: Stack Overflow, メーリングリストなど)
    • AI_ADDRCONFIGの挙動に関する一般的な情報: https://stackoverflow.com/questions/10049586/what-does-ai-addrconfig-do
    • LinuxにおけるgetaddrinfoAI_ADDRCONFIGの特定の挙動に関する情報(一般的な検索結果に基づく)
      • getaddrinfoAI_ADDRCONFIGフラグがLinuxで問題を引き起こす可能性については、Goのコミットメッセージに明記されており、これは当時のGo開発チームが直面した具体的な問題を示しています。一般的なgetaddrinfoのドキュメントでは、この特定の挙動の落とし穴が詳細に説明されていない場合があります。