[インデックス 17530] ファイルの概要
このコミットは、Go言語のビルドシステムにおいて、cgo
ディレクティブのLDFLAGS
オプションで$
記号の使用を許可するように変更するものです。具体的には、リンカオプションとしてよく用いられる$ORIGIN
のような特殊な変数を使用できるようにすることで、共有ライブラリのパス解決における柔軟性を向上させます。これにより、特にLinux環境での動的リンクライブラリの扱いが改善され、よりポータブルなGoアプリケーションのビルドが可能になります。
コミット
commit c971f95c10b9ee79ab4c5aab2cff4e2cb642fb72
Author: Russ Cox <rsc@golang.org>
Date: Tue Sep 10 12:47:43 2013 -0400
go/build: allow $ in cgo LDFLAGS
Fixes #6038.
R=iant
CC=golang-dev
https://golang.org/cl/13649043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c971f95c10b9ee79ab4c5aab2cff4e2cb642fb72
元コミット内容
--- a/src/cmd/go/test.bash
+++ b/src/cmd/go/test.bash
@@ -483,6 +483,25 @@ fi
rm -rf $d
unset GOPATH
+TEST 'cgo handles -Wl,$ORIGIN'
+d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX)
+export GOPATH=$d
+mkdir -p $d/src/origin
+echo '
+package origin
+// #cgo LDFLAGS: -Wl,-rpath -Wl,$ORIGIN
+// void f(void) {}
+import "C"
+
+func f() { C.f() }
+' >$d/src/origin/origin.go
+if ! ./testgo build origin; then
+ echo build failed
+ ok=false
+fi
+rm -rf $d
+unset GOPATH
+
# clean up
if $started; then stop; fi
rm -rf testdata/bin testdata/bin1
diff --git a/src/pkg/go/build/build.go b/src/pkg/go/build/build.go
index 043351a950..1b62c3da89 100644
--- a/src/pkg/go/build/build.go
+++ b/src/pkg/go/build/build.go
@@ -920,7 +920,7 @@ func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup)
return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig)
}
for _, arg := range args {
- if !safeName(arg) {
+ if !safeCgoName(arg) {
return fmt.Errorf("%s: malformed #cgo argument: %s", filename, arg)
}\n }\n@@ -943,9 +943,12 @@ func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup)
return nil
}
-var safeBytes = []byte("+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:")
+// NOTE: $ is not safe for the shell, but it is allowed here because of linker options like -Wl,$ORIGIN.
+// We never pass these arguments to a shell (just to programs we construct argv for), so this should be okay.\n// See golang.org/issue/6038.\n+var safeBytes = []byte("+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:$")
-func safeName(s string) bool {
+func safeCgoName(s string) bool {
if s == "" {
return false
}
変更の背景
この変更は、Goのビルドシステムがcgo
ディレクティブ内で$
記号を含むリンカフラグ(特に-Wl,$ORIGIN
)を正しく処理できるようにするために行われました。以前のバージョンでは、cgo
の引数として渡される文字列の安全性を検証するsafeName
関数が$
記号を不正な文字として扱っていたため、$ORIGIN
のような動的リンカの特殊な変数をLDFLAGS
に含めることができませんでした。
$ORIGIN
は、実行ファイル自身のパスを基準とした相対パスで共有ライブラリを検索するために非常に有用な機能です。特に、アプリケーションとそれに依存する共有ライブラリを単一のディレクトリにまとめて配布するようなポータブルなアプリケーションを作成する際に不可欠です。この制限により、Goでcgo
を使用する際に、このような柔軟な共有ライブラリのパス解決が妨げられていました。
コミットメッセージに「Fixes #6038」とあることから、この問題はGoのIssueトラッカーで報告されており、ユーザーからの要望があったことが伺えます。
前提知識の解説
cgo
cgo
はGo言語のツールの一つで、GoプログラムからC言語のコードを呼び出したり、逆にC言語のコードからGoの関数を呼び出したりすることを可能にします。Goのソースファイル内でimport "C"
と記述することでcgo
が有効になり、Cの関数やデータ型をGoのコードから利用できるようになります。cgo
はビルド時にCコンパイラとGoコンパイラを連携させ、GoとCの間のインターフェースコードを生成します。
LDFLAGS
LDFLAGS
は、ビルドシステムで一般的に使用される変数で、リンカに渡すオプションを指定するために使われます。Goのcgo
においては、#cgo LDFLAGS: ...
というディレクティブを使って、Cコードをリンクする際にリンカに渡す追加のフラグを指定します。これらのフラグは、ライブラリの検索パス(-L
)、リンクするライブラリ(-l
)、その他のリンカ固有のオプション(-Wl,...
)などを指定するために用いられます。
-Wl,
-Wl,option
は、GCCやClangなどのコンパイラドライバ(コンパイラ、アセンブラ、リンカなどを呼び出すプログラム)に渡されるオプションです。このオプションは、コンマの後に続くoption
を直接リンカに渡すように指示します。例えば、-Wl,-rpath,/path/to/lib
は、リンカに-rpath,/path/to/lib
というオプションを渡し、実行時に/path/to/lib
を共有ライブラリの検索パスに追加するように指示します。
$ORIGIN
$ORIGIN
は、主にLinuxやUnix系システムで使われる動的リンカの特殊なトークンです。実行時に、動的リンカはこの$ORIGIN
を、現在実行されている実行ファイルが格納されているディレクトリの絶対パスに置き換えます。
例えば、/usr/local/bin/myapp
という実行ファイルがあり、その実行ファイルが/usr/local/bin/lib
にある共有ライブラリに依存している場合、LDFLAGS
に-Wl,-rpath,$ORIGIN/lib
と指定することで、実行ファイルがどこに移動されても、常に自身のディレクトリ内のlib
サブディレクトリから共有ライブラリを検索するようになります。これは、アプリケーションのポータビリティを高める上で非常に強力な機能です。
技術的詳細
このコミットの核心は、src/pkg/go/build/build.go
ファイル内のsafeCgoName
関数(変更前はsafeName
)とsafeBytes
変数の変更にあります。
以前のsafeName
関数は、#cgo
ディレクティブの引数として渡される文字列が安全であるか(シェルにとって危険な文字を含んでいないか)を検証していました。この「安全」の定義には、$
記号が含まれていませんでした。これは、$
がシェルにおいて変数展開などの特殊な意味を持つため、意図しないコマンド実行やセキュリティ上の脆弱性を引き起こす可能性があったためです。
しかし、リンカオプションとして-Wl,$ORIGIN
のように$
記号を含む文字列は、シェルによって解釈されるのではなく、直接リンカに渡されるため、シェルにとっての「安全」の定義とは異なる文脈で評価されるべきです。Goのビルドシステムは、これらの引数をシェルを介さずに直接プログラム(リンカ)に渡すため、$
記号が含まれていても問題ありません。
このコミットでは、safeBytes
配列に$
記号を追加することで、safeCgoName
関数が$
記号を有効な文字として認識するように変更されました。これにより、cgo LDFLAGS
で$ORIGIN
のようなリンカオプションが使用できるようになり、Goアプリケーションのビルドにおける柔軟性が向上しました。
同時に、src/cmd/go/test.bash
に新しいテストケースが追加され、cgo
が-Wl,$ORIGIN
を正しく処理できることを検証しています。このテストは、$ORIGIN
を含むLDFLAGS
ディレクティブを持つGoパッケージをビルドし、ビルドが成功することを確認します。
コアとなるコードの変更箇所
src/cmd/go/test.bash
TEST 'cgo handles -Wl,$ORIGIN'
という新しいテストケースが追加されました。- このテストケースでは、
#cgo LDFLAGS: -Wl,-rpath -Wl,$ORIGIN
を含むorigin.go
ファイルを作成し、go build origin
コマンドが成功するかどうかを検証しています。
src/pkg/go/build/build.go
safeName
関数がsafeCgoName
にリネームされました。safeBytes
バイト配列に$
記号が追加されました。- 変更前:
var safeBytes = []byte("+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:")
- 変更後:
var safeBytes = []byte("+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:$")
- 変更前:
safeCgoName
関数を呼び出す箇所が、リネームに合わせて更新されました。safeBytes
の定義の上に、$
記号がシェルにとって安全ではないが、リンカオプションとして許可される理由と、golang.org/issue/6038
への参照を含むコメントが追加されました。
コアとなるコードの解説
src/cmd/go/test.bash
の変更
追加されたテストケースは、この変更が意図通りに機能することを確認するためのものです。
- 一時ディレクトリを作成し、
GOPATH
として設定します。 src/origin/origin.go
というファイルを作成します。このファイルには、// #cgo LDFLAGS: -Wl,-rpath -Wl,$ORIGIN
というcgo
ディレクティブが含まれています。これは、cgo
がリンカに-rpath
オプションと、実行ファイル自身のパスを示す$ORIGIN
を渡すように指示します。./testgo build origin
コマンドを実行し、origin
パッケージがエラーなくビルドできることを確認します。ビルドが失敗した場合、テストはok=false
を設定し、最終的にテスト全体が失敗します。 このテストの追加により、将来の変更がこの$ORIGIN
のサポートを誤って壊すことがないように保証されます。
src/pkg/go/build/build.go
の変更
-
safeName
からsafeCgoName
へのリネーム: このリネームは、この関数がcgo
の引数に特化した安全性のチェックを行うことをより明確にするためのものです。一般的な「安全な名前」のチェックとは異なり、cgo
の文脈では特定の特殊文字(この場合は$
)が許可されるため、関数名を特化させることでコードの意図が伝わりやすくなります。 -
safeBytes
への$
記号の追加: これがこのコミットの最も重要な変更点です。safeBytes
は、cgo
の引数として許可される文字のホワイトリストを定義しています。以前は$
が含まれていなかったため、$ORIGIN
のような文字列は不正な引数として扱われ、ビルドエラーとなっていました。$
を追加することで、safeCgoName
関数は$
を含む文字列を有効と判断し、ビルドが続行できるようになります。 -
追加されたコメント:
safeBytes
の定義の上に追加されたコメントは、この変更の背景にある重要な設計判断を説明しています。// NOTE: $ is not safe for the shell, but it is allowed here because of linker options like -Wl,$ORIGIN.
// We never pass these arguments to a shell (just to programs we construct argv for), so this should be okay.
// See golang.org/issue/6038.
このコメントは、$
がシェルスクリプトでは特殊な意味を持つため通常は避けるべき文字であるにもかかわらず、なぜここで許可されるのかを明確にしています。その理由は、cgo
がこれらの引数をシェルを介さずに直接リンカプログラムに渡すため、シェルの解釈によるセキュリティリスクがないためです。また、関連するIssue番号#6038
への参照も含まれており、この変更の経緯を追跡するのに役立ちます。
これらの変更により、Goのビルドシステムはcgo
と外部リンカとの連携において、より高度な機能($ORIGIN
のような動的リンカの特殊変数)をサポートできるようになり、Goアプリケーションのデプロイメントとポータビリティが向上します。
関連リンク
- Go Issue 6038 (コミットメッセージに記載されているが、直接的なGoのIssueトラッカーのリンクは今回の検索では見つからなかった。通常は
https://go.dev/issue/6038
のような形式で存在すると考えられる。)
参考にした情報源リンク
- cgo - Go Programming Language
- Go Command Documentation - go build
- GCC Linker Options - -Wl,option (
-Wl,
に関する情報) - What is $ORIGIN, $LIB and $PLATFORM? (
$ORIGIN
に関するStack Overflowの議論) - Using $ORIGIN in rpath (
$ORIGIN
とrpath
に関するStack Overflowの議論) - Ubuntu Security Notice USN-6038-1 (検索結果で
golang issue 6038
に関連して表示されたが、本コミットとは直接関係ないセキュリティアドバイザリ) - Go issue 6038 - vulners.com (検索結果で
golang issue 6038
に関連して表示されたが、本コミットとは直接関係ないセキュリティアドバイザリ)