[インデックス 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)を誤らせる可能性があるため、意図的に除外されています。これは、単に定数が存在しないという問題だけでなく、定数のセマンティクスがプラットフォーム間で異なる場合にも対応する必要があることを示唆しています。
前提知識の解説
このコミットを理解するためには、以下の技術的な概念について理解しておく必要があります。
-
Cgo:
- Cgoは、GoプログラムからC言語のコードを呼び出すためのGoの機能です。Goの
net
パッケージのような低レベルのネットワーク操作では、OSが提供するC言語のシステムコール(例:getaddrinfo
)を直接呼び出す必要がある場合があり、その際にCgoが利用されます。 - Cgoを使用すると、Goのコード内でCのヘッダーファイルをインポートし、Cの関数や構造体を利用できます。
- Cgoは、GoプログラムからC言語のコードを呼び出すためのGoの機能です。Goの
-
getaddrinfo
システムコール:getaddrinfo
は、ホスト名とサービス名(ポート番号など)から、ネットワークアドレス構造体(sockaddr
構造体)のリストを取得するための標準的なC言語のAPIです。これは、アプリケーションがネットワーク接続を確立する際に、IPアドレスやポート番号を解決するために広く使用されます。- この関数は、
hints
という構造体を引数として受け取り、その中に検索の挙動を制御するためのフラグ(ai_flags
)を設定できます。
-
AI_*
フラグ:getaddrinfo
のai_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アドレスを返さない、といった挙動になります。このフラグは、ネットワーク設定に依存した挙動をするため、注意が必要です。
-
プラットフォーム固有のコード:
- Go言語では、ビルドタグ(build tags)を使用して、特定のオペレーティングシステムやアーキテクチャに特化したコードを記述することができます。例えば、
// +build linux
という行がファイルの先頭にある場合、そのファイルはLinuxシステムでのみコンパイルされます。 - このコミットでは、
cgo_bsd.go
、cgo_linux.go
、cgo_unix.go
といったファイル名が使用されており、これらはそれぞれBSD系OS、Linux、一般的なUnix系OS向けのCgo関連コードを格納していることを示唆しています。これにより、OSごとの差異を吸収し、コードの移植性を高めています。
- Go言語では、ビルドタグ(build tags)を使用して、特定のオペレーティングシステムやアーキテクチャに特化したコードを記述することができます。例えば、
技術的詳細
このコミットの技術的な核心は、getaddrinfo
システムコールに渡すai_flags
の値を、プラットフォームごとに異なるGoの関数で提供するように変更した点です。
以前は、src/pkg/net/cgo_unix.go
のような汎用的なUnix向けファイルで、すべてのAI_*
フラグがハードコードされていました。しかし、これはNetBSDやOpenBSDのような一部のプラットフォームで問題を引き起こしました。これらのOSでは、Goが期待するAI_*
定数の一部(例えばAI_ALL
やAI_V4MAPPED
など、あるいはその組み合わせ)がCライブラリに存在しないか、異なる値を持つ可能性がありました。その結果、コンパイルエラーや予期せぬランタイムエラーが発生し、Cgoを利用したネットワーク機能が正しく動作しませんでした。
この問題を解決するため、コミットでは以下の変更が行われました。
-
cgoAddrInfoMask()
からcgoAddrInfoFlags()
への変更:- 以前は
cgoAddrInfoMask()
という関数があり、これはAI_MASK
という定数を返していました。このAI_MASK
が具体的に何を意味していたかは不明ですが、おそらく利用可能なフラグのビットマスクとして機能していた可能性があります。 - 新しいアプローチでは、
cgoAddrInfoFlags()
という関数が導入され、これはgetaddrinfo
に直接渡すべきai_flags
の値を返します。これにより、各プラットフォームが独自のフラグセットを定義できるようになります。
- 以前は
-
プラットフォーム固有のフラグ定義:
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つのファイルが変更されています。
-
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 }
-
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 }
-
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.go
とsrc/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がどのように対応しているかを示す良い例です。
- これらのファイルは、それぞれのOS向けに特化した
-
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言語のCgoに関する公式ドキュメント: https://go.dev/cmd/cgo/
getaddrinfo
に関するPOSIX標準(manページなど):man getaddrinfo
(Unix/Linuxシステムで実行可能)- オンラインリファレンス例: https://man7.org/linux/man-pages/man3/getaddrinfo.3.html
参考にした情報源リンク
- 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における
getaddrinfo
とAI_ADDRCONFIG
の特定の挙動に関する情報(一般的な検索結果に基づく)getaddrinfo
のAI_ADDRCONFIG
フラグがLinuxで問題を引き起こす可能性については、Goのコミットメッセージに明記されており、これは当時のGo開発チームが直面した具体的な問題を示しています。一般的なgetaddrinfo
のドキュメントでは、この特定の挙動の落とし穴が詳細に説明されていない場合があります。