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

[インデックス 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の変更

追加されたテストケースは、この変更が意図通りに機能することを確認するためのものです。

  1. 一時ディレクトリを作成し、GOPATHとして設定します。
  2. src/origin/origin.goというファイルを作成します。このファイルには、// #cgo LDFLAGS: -Wl,-rpath -Wl,$ORIGINというcgoディレクティブが含まれています。これは、cgoがリンカに-rpathオプションと、実行ファイル自身のパスを示す$ORIGINを渡すように指示します。
  3. ./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のような形式で存在すると考えられる。)

参考にした情報源リンク