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

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

このコミットは、Go言語のcgoツールに関連する変更です。具体的には、src/cmd/cgo/out.goファイルが修正されています。このファイルは、cgoがCコードをGoコードと連携させるために生成するCのラッパーコードの出力ロジックを扱っています。特に、構造体のメモリレイアウトやアライメントに関する属性の付与に関わる部分が変更されています。

コミット

cmd/cgo: __gcc_struct__属性を使用しないことでclangを満足させる。

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

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

元コミット内容

cmd/cgo: makes clang happy by not using __gcc_struct__ attribute.

R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/10150043

変更の背景

このコミットの背景には、cgoが生成するCコードが特定のコンパイラ(特にClang)で問題を引き起こす可能性があったという事情があります。

元々、cgoはGoの構造体とCの構造体間でデータをやり取りする際に、メモリレイアウトの一貫性を保つために__attribute__((__packed__))というGCC拡張属性を使用していました。さらに、特定のGCCのバグ(GCC PR52991)やGoの内部的な問題(Go issue 5603)を回避するために、__gcc_struct__という属性も併用されていました。

しかし、この__gcc_struct__属性はGCC特有のものであり、Clangのような他のCコンパイラでは認識されないか、異なる解釈をされる可能性がありました。結果として、Clangでコンパイルする際に警告やエラーが発生し、ビルドプロセスが中断される、あるいは予期せぬ動作を引き起こす可能性がありました。

このコミットは、Clangを使用している環境でもcgoが生成するコードが問題なくコンパイルされるように、__gcc_struct__属性の適用をGCCに限定することで、コンパイラの互換性問題を解決することを目的としています。

前提知識の解説

cgo

cgoはGo言語に組み込まれているツールで、GoプログラムからC言語のコードを呼び出したり、逆にC言語のコードからGoの関数を呼び出したりするためのメカニズムを提供します。これにより、既存のCライブラリをGoプロジェクトで再利用したり、パフォーマンスが重要な部分をCで記述したりすることが可能になります。cgoは、GoとCの間のデータ型変換や関数呼び出しのための接着コード(binding code)を生成します。

__attribute__((__packed__))

これはGCC(GNU Compiler Collection)およびClangがサポートするコンパイラ拡張属性の一つです。構造体や共用体にこの属性を付与すると、コンパイラはメンバー間のパディング(メモリのアライメントのために挿入される未使用バイト)を最小限に抑え、可能な限りメモリを密に配置しようとします。これにより、構造体のサイズが小さくなり、CとGoのような異なる言語間で構造体を共有する際に、メモリレイアウトの不一致による問題を避けるのに役立ちます。

__gcc_struct__属性

これもGCCのコンパイラ拡張属性の一つです。この属性は、特定の構造体に対してGCCのデフォルトの構造体パディングルールを強制するために使用されることがあります。通常、__packed__属性と組み合わせて使用されることが多く、特定のコンパイラのバグや挙動の差異を回避するために用いられることがあります。このコミットの文脈では、GCCの特定のバグ(PR52991)とGoの関連する問題(issue 5603)のワークアラウンドとして使用されていました。

GCC PR52991

これはGCCのバグトラッカーに報告された問題で、printfscanfといった標準入出力関数が__int128型(128ビット整数型)を正しく扱えないというものです。このバグは、特に__int128型を含む構造体が__packed__属性を持つ場合に、メモリレイアウトの解釈に影響を与える可能性がありました。__gcc_struct__属性は、この問題に対する特定のワークアラウンドとして導入された可能性があります。

Go issue 5603

このGoのイシューは、cgoと構造体のパディング、特に__packed__属性に関連する問題を示唆しています。具体的な内容はコミットメッセージからは読み取れませんが、GCC PR52991と同様に、異なるコンパイラやアーキテクチャ間での構造体のアライメントとパディングの挙動の不一致が原因で発生する問題であったと推測されます。

Clang

Clangは、LLVMプロジェクトの一部として開発されているC、C++、Objective-C、Objective-C++コンパイラのフロントエンドです。GCCと互換性のある多くのコンパイラ拡張をサポートしていますが、すべてのGCC拡張を完全にサポートしているわけではありません。特に、__gcc_struct__のようなGCC固有の属性は、Clangでは認識されないか、異なる警告やエラーを引き起こす可能性があります。

技術的詳細

このコミットが行われる前は、cgoはGoのamd64および386アーキテクチャ向けにCコードを生成する際に、常に__attribute__((__packed__, __gcc_struct__))という属性を構造体に付与していました。これは、前述のGCCのバグ(PR52991)とGoのイシュー(5603)に対するワークアラウンドとして機能していました。

しかし、このアプローチはClangコンパイラを使用する環境で問題を引き起こしました。Clangは__gcc_struct__属性を認識しないため、コンパイル時に警告やエラーを発生させ、ビルドの失敗につながる可能性がありました。

このコミットの解決策は、__gcc_struct__属性を適用する条件をより厳密にすることです。具体的には、cgoが現在使用しているCコンパイラがClangではない場合にのみ、この属性を付与するように変更されました。これにより、GCCを使用している環境では引き続きワークアラウンドが適用され、Clangを使用している環境では不要な属性が取り除かれ、コンパイルが正常に行われるようになります。

この変更は、cgoが内部的に使用するCコンパイラの名前をチェックし、その名前が"clang"を含まない場合にのみ__gcc_struct__属性を追加するというロジックによって実現されています。

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

--- a/src/cmd/cgo/out.go
+++ b/src/cmd/cgo/out.go
@@ -488,7 +488,7 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
 	// Use __gcc_struct__ to work around http://gcc.gnu.org/PR52991 on x86,
 	// and http://golang.org/issue/5603.
 	extraAttr := ""
-	if goarch == "amd64" || goarch == "386" {
+	if !strings.Contains(p.gccName(), "clang") && (goarch == "amd64" || goarch == "386") {
 		extraAttr = ", __gcc_struct__"
 	}
 	fmt.Fprintf(fgcc, "\t%s __attribute__((__packed__%v)) *a = v;\\n", ctype, extraAttr)

コアとなるコードの解説

変更はsrc/cmd/cgo/out.goファイルのwriteOutputFunc関数内で行われています。

元のコードでは、extraAttrという文字列変数が、goarch(Goのターゲットアーキテクチャ)がamd64または386の場合にのみ", __gcc_struct__"という値に設定されていました。これは、x86系のアーキテクチャでGCCのバグとGoのイシューを回避するためのものでした。

変更後のコードでは、この条件に新たなチェックが追加されています。 !strings.Contains(p.gccName(), "clang")

  • p.gccName(): これはcgoが現在使用しているCコンパイラの名前を返すメソッドです。例えば、"gcc"や"clang"といった文字列が返されます。
  • strings.Contains(p.gccName(), "clang"): これは、コンパイラ名に"clang"という部分文字列が含まれているかどうかをチェックします。
  • !strings.Contains(...): この!は論理否定であり、「コンパイラ名に"clang"が含まれていない場合」という条件を意味します。

したがって、新しいif文の条件は以下のようになります。 「使用しているCコンパイラがClangではなく、かつターゲットアーキテクチャがamd64または386である場合

この条件が真である場合にのみ、extraAttr", __gcc_struct__"が設定され、結果として生成されるCコードの構造体に__gcc_struct__属性が付与されます。これにより、Clangを使用している環境ではこの属性が生成されなくなり、コンパイルエラーや警告が回避されるようになります。

関連リンク

  • Go issue 5603: https://golang.org/issue/5603 (このリンクはコミットメッセージに記載されていますが、現在のGoのイシュートラッカーでは直接見つからない可能性があります。古いイシューはアーカイブされているか、別の番号に統合されている場合があります。)
  • GCC Bug 52991: http://gcc.gnu.org/PR52991 (これは__int128printf/scanfに関するバグです。)
  • Go CL 10150043: https://golang.org/cl/10150043 (Gerrit Code Reviewの変更リストへのリンク)

参考にした情報源リンク