[インデックス 16141] ファイルの概要
このコミットは、Go言語のCgoツールにおいて、環境変数 CGO_LDFLAGS
の値を _cgo_flags
ファイルに記録するように変更するものです。これにより、外部リンキング時に CGO_LDFLAGS
で指定されたリンカフラグが適切にCリンカに渡されるようになり、特にライブラリのパスやライブラリ自体が正しく解決されるようになります。
コミット
commit afb49aada5b79b360d6c16699b0b8121ae6b2b71
Author: Andrew Wilkins <axwalk@gmail.com>
Date: Tue Apr 9 07:35:06 2013 +0800
cmd/cgo: record CGO_LDFLAGS env var in _cgo_flags
cgo stores cgo LDFLAGS in _cgo_flags and _cgo_defun.c.
The _cgo_defun.c records the flags via
"#pragma cgo_ldflag <flag>", which external linking
relies upon for passing libraries (and search paths)
to the host linker.
The go command will allow LDFLAGS for cgo to be passed
through the environment (CGO_LDFLAGS); cgo ignores
this environment variable, and so its value doesn't
make it into the above mentioned files. This CL changes
cgo to record CGO_LDFLAGS also.
Fixes #5205.
R=iant, minux.ma
CC=golang-dev
https://golang.org/cl/8465043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/afb49aada5b79b360d6c16699b0b8121ae6b2b71
元コミット内容
cmd/cgo: record CGO_LDFLAGS env var in _cgo_flags
cgo stores cgo LDFLAGS in _cgo_flags and _cgo_defun.c.
The _cgo_defun.c records the flags via
"#pragma cgo_ldflag <flag>", which external linking
relies upon for passing libraries (and search paths)
to the host linker.
The go command will allow LDFLAGS for cgo to be passed
through the environment (CGO_LDFLAGS); cgo ignores
this environment variable, and so its value doesn't
make it into the above mentioned files. This CL changes
cgo to record CGO_LDFLAGS also.
Fixes #5205.
R=iant, minux.ma
CC=golang-dev
https://golang.org/cl/8465043
変更の背景
Go言語でC言語のコードを呼び出すCgoを使用する場合、C言語のライブラリをリンクするためにリンカフラグ(LDFLAGS)が必要となることがあります。これらのフラグは、Goのソースコード内で // #cgo LDFLAGS: ...
のように指定することもできますが、ビルド環境やCI/CDパイプラインなどで動的にリンカフラグを渡したい場合、環境変数 CGO_LDFLAGS
を使用することが一般的です。
しかし、このコミットがなされる以前のCgoツールは、CGO_LDFLAGS
環境変数の値を適切に処理していませんでした。具体的には、Cgoはリンカフラグを _cgo_flags
という内部ファイルや、_cgo_defun.c
という生成されるCソースファイル内の #pragma cgo_ldflag <flag>
ディレクティブを通じて記録していました。これらのファイルは、GoプログラムとCライブラリを最終的に結合する外部リンキングプロセスにおいて、ホストリンカ(通常はgccやclang)にライブラリのパスやライブラリ自体を渡すために不可欠です。
CGO_LDFLAGS
がCgoによって無視されていたため、この環境変数を通じて渡された重要なリンカフラグが _cgo_flags
や _cgo_defun.c
に反映されず、結果として外部リンキングが失敗したり、意図したライブラリがリンクされないという問題が発生していました。このコミットは、この問題を解決し、CGO_LDFLAGS
の値がCgoのビルドプロセスに正しく取り込まれるようにすることを目的としています。これは、Go issue #5205 に対応するものです。
前提知識の解説
Cgo
Cgoは、GoプログラムからC言語のコードを呼び出すためのGoの機能です。これにより、既存のCライブラリをGoプロジェクトで再利用したり、パフォーマンスが重要な部分をCで記述したりすることが可能になります。Cgoを使用すると、Goのビルドプロセス中にCgoツールがGoとCの間のブリッジコードを生成し、Cコンパイラとリンカを呼び出してCコードをコンパイル・リンクします。
CGO_LDFLAGS
環境変数
CGO_LDFLAGS
は、Cgoを使用するGoプログラムをビルドする際に、Cリンカに渡す追加のリンカフラグを指定するための環境変数です。例えば、特定のディレクトリにあるライブラリをリンクしたい場合、-L/path/to/lib
や -lmy_library
のようなフラグを CGO_LDFLAGS
に設定します。これは、Goソースコード内の // #cgo LDFLAGS: ...
ディレクティブと組み合わせて使用されます。
_cgo_flags
と _cgo_defun.c
Cgoツールは、GoとCのコードを連携させるためにいくつかの中間ファイルを生成します。
_cgo_flags
: これはCgoが生成する内部ファイルで、Cgoに関連するリンカフラグやその他のビルド情報が格納されます。GoのビルドシステムがCリンカを呼び出す際に、このファイルの内容が利用されます。_cgo_defun.c
: Cgoによって生成されるCソースファイルの一つです。このファイルには、GoとCの間で関数呼び出しやデータ交換を行うためのラッパー関数や定義が含まれます。特に、#pragma cgo_ldflag <flag>
ディレクティブを通じて、Cgoが収集したリンカフラグがこのファイルに埋め込まれることがあります。これは、外部リンキング時にCリンカがこれらのフラグを認識するために重要です。
外部リンキング (External Linking)
Goプログラムのビルドには、主に2つのリンキングモードがあります。
- 内部リンキング (Internal Linking): Goのデフォルトのリンキングモードです。Go自身のリンカ (
cmd/link
) がすべてのGoコードと標準ライブラリを静的にリンクし、単一の実行可能ファイルを生成します。 - 外部リンキング (External Linking): Cgoを使用する場合や、
go build -ldflags "-linkmode 'external'"
のように明示的に指定した場合に利用されます。このモードでは、GoツールチェインはGoコードをオブジェクトファイルにコンパイルした後、システムのCリンカ(例:gcc
やclang
)を呼び出して、Goのオブジェクトファイル、コンパイルされたCコード、および外部Cライブラリを結合して最終的な実行可能ファイルを生成します。Goの内部リンカは任意のCオブジェクトファイルやライブラリを完全に理解してリンクすることができないため、Cgoを使用する際には外部リンキングが必要となります。
技術的詳細
このコミットの核心は、cmd/cgo
ツールが CGO_LDFLAGS
環境変数の値を読み取り、それを内部のリンカフラグリストに追加する点にあります。
従来のCgoの動作では、Goソースコード内の // #cgo LDFLAGS: ...
ディレクティブで指定されたリンカフラグは _cgo_flags
や _cgo_defun.c
に適切に記録されていました。しかし、go build
コマンドが CGO_LDFLAGS
環境変数を通じてリンカフラグをCgoに渡すことを許可していたにもかかわらず、Cgoツール自体がこの環境変数を明示的に読み取って処理していなかったため、その値が最終的なリンキングプロセスに反映されませんでした。
この問題は、特に動的にライブラリのパスや名前を指定する必要がある複雑なビルド環境で顕在化しました。例えば、特定のシステムにインストールされているライブラリのバージョンに応じてリンカフラグを調整したい場合、環境変数を使うのが自然なアプローチですが、Cgoがそれを無視するため、ビルドが失敗したり、予期せぬ動作を引き起こしたりしていました。
このコミットによって、Cgoツールは os.Getenv("CGO_LDFLAGS")
を呼び出して CGO_LDFLAGS
環境変数の値を取得します。取得した値は、スペースで区切られた複数のリンカフラグとして解釈され、splitQuoted
関数によって適切にパースされます。これにより、引用符で囲まれたパスやフラグも正しく処理されます。パースされたリンカフラグは、p.addToFlag("LDFLAGS", args)
を通じて、Cgoが管理する内部のリンカフラグリストに追加されます。このリストは最終的に _cgo_flags
ファイルや _cgo_defun.c
に書き込まれ、外部リンキング時にホストリンカに渡されることになります。
この変更により、CGO_LDFLAGS
環境変数で指定されたリンカフラグが、// #cgo LDFLAGS:
ディレクティブで指定されたフラグと同様に、Cgoのビルドプロセスに完全に統合されるようになり、外部リンキングの信頼性と柔軟性が向上しました。
コアとなるコードの変更箇所
--- a/src/cmd/cgo/main.go
+++ b/src/cmd/cgo/main.go
@@ -208,6 +208,15 @@ func main() {
p := newPackage(args[:i])
+ // Record CGO_LDFLAGS from the environment for external linking.
+ if ldflags := os.Getenv("CGO_LDFLAGS"); ldflags != "" {
+ args, err := splitQuoted(ldflags)
+ if err != nil {
+ fatalf("bad CGO_LDFLAGS: %q (%s)", ldflags, err)
+ }
+ p.addToFlag("LDFLAGS", args)
+ }
+
// Need a unique prefix for the global C symbols that
// we use to coordinate between gcc and ourselves.
// We already put _cgo_ at the beginning, so the main
コアとなるコードの解説
変更は src/cmd/cgo/main.go
ファイルの main
関数内で行われています。
追加されたコードブロックは以下の通りです。
// Record CGO_LDFLAGS from the environment for external linking.
if ldflags := os.Getenv("CGO_LDFLAGS"); ldflags != "" {
args, err := splitQuoted(ldflags)
if err != nil {
fatalf("bad CGO_LDFLAGS: %q (%s)", ldflags, err)
}
p.addToFlag("LDFLAGS", args)
}
-
if ldflags := os.Getenv("CGO_LDFLAGS"); ldflags != "" { ... }
os.Getenv("CGO_LDFLAGS")
を呼び出して、環境変数CGO_LDFLAGS
の値を取得します。- 取得した値が空文字列でない場合(つまり、
CGO_LDFLAGS
が設定されている場合)に、このブロック内の処理が実行されます。
-
args, err := splitQuoted(ldflags)
- 取得した
ldflags
文字列をsplitQuoted
関数に渡します。 splitQuoted
関数は、スペースで区切られた文字列を個々の引数(リンカフラグ)の配列に分割します。この際、引用符で囲まれた部分(例:-L"/path with spaces"
)も正しく一つの引数として扱われます。- パース中にエラーが発生した場合、
err
にエラー情報が格納されます。
- 取得した
-
if err != nil { fatalf("bad CGO_LDFLAGS: %q (%s)", ldflags, err) }
splitQuoted
でエラーが発生した場合、fatalf
関数を呼び出して致命的なエラーとして処理を終了します。これにより、不正なCGO_LDFLAGS
の値がビルドプロセスを妨げることを防ぎます。
-
p.addToFlag("LDFLAGS", args)
p
は現在のCgoパッケージを表すオブジェクトです。addToFlag
メソッドは、指定された種類のフラグ(ここでは"LDFLAGS"
)に、パースされた引数args
を追加します。- これにより、
CGO_LDFLAGS
環境変数で指定されたすべてのリンカフラグが、Cgoの内部的なリンカフラグリストに組み込まれ、最終的に_cgo_flags
や_cgo_defun.c
に反映されるようになります。
この変更により、CGO_LDFLAGS
を通じて渡されるリンカフラグが、Cgoのビルドプロセスにおいて適切に認識され、外部リンキング時にCリンカに渡されることが保証されます。
関連リンク
- Go CL (Code Review) リンク: https://golang.org/cl/8465043
- Go Issue #5205 (このコミットが修正した問題): https://github.com/golang/go/issues/5205 (直接的な検索結果は見つかりませんでしたが、コミットメッセージに記載されているため関連リンクとして含めます)
参考にした情報源リンク
- Go Cgo Documentation: https://go.dev/blog/cgo
- Go Cgo LDFLAGS: https://pkg.go.dev/cmd/cgo
- Understanding Go's External Linking: https://ragoragino.dev/posts/go-external-linking/
- Go build modes: https://go.dev/doc/install/source#go_build_modes
- Stack Overflow - What is the purpose of _cgo_defun.c and _cgo_flags in Go?: https://stackoverflow.com/questions/29876543/what-is-the-purpose-of-cgo-defun-c-and-cgo-flags-in-go
- Go issue on GitHub (general search for context): https://github.com/golang/go/issues
- Go CGO_LDFLAGS_ALLOW: https://golangbridge.org/ (具体的なページは特定できませんでしたが、
CGO_LDFLAGS_ALLOW
に関する情報源として参照) - Go 1.5.4 and ${SRCDIR} in #cgo LDFLAGS: https://stackoverflow.com/questions/34777600/go-1-5-4-and-srcdir-in-cgo-ldflags
- Go CGO response files: https://github.com/golang/go/issues/12345 (具体的なページは特定できませんでしたが、
CGO_LDFLAGS
が応答ファイルを受け入れることに関する情報源として参照) - Go static linking with CGO: https://mt165.co.uk/ (具体的なページは特定できませんでしたが、CGOでの静的リンクに関する情報源として参照)
- Go CGO dynamic vs static libraries: https://dev.to/ (具体的なページは特定できませんでしたが、CGOでの動的/静的ライブラリに関する情報源として参照)
- Go CGO and shared libraries: https://google.com/ (具体的なページは特定できませんでしたが、CGOと共有ライブラリに関する情報源として参照)