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

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

このコミットは、Go言語のツールチェインにおけるcgo(C言語との相互運用機能)、cmd/dist(Goのビルドシステム)、cmd/go(Goコマンド)がclangコンパイラと連携する際の複数の問題を修正することを目的としています。特に、clangの診断メッセージの挙動、libgccのパス解決、DWARFデバッグ情報の生成、および特定の警告オプションに関する修正が含まれています。

コミット

commit eec961470fc38a253b46b4004f9acb72741e93d7
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Sat Feb 23 20:24:38 2013 +0800

    cmd/cgo, cmd/dist, cmd/go: cgo with clang fixes
    1. Workaround the smart clang diagnostics with -Qunused-arguments:
    clang: error: argument unused during compilation: '-XXX'
    2. if "clang -print-libgcc-file-name" returns non-absolute path, don't
    provide that on linker command line.
    3. Fix dwarf.PtrType.Size() in cmd/cgo as clang doesn't generate
    DW_AT_byte_size for pointer types.
    4. Workaround warnings for -Wno-unneeded-internal-declaration with
    -Wno-unknown-warning-option.
    5. Add -Wno-unused-function.
    6. enable race detector test on darwin with clang
    (at least Apple clang version 1.7 (tags/Apple/clang-77) works).
    
    Requires CL 7354043.
    
    Update #4829
    This should fix most parts of the problem, but one glitch still remains.
    DWARF generated by newer clang doesn't differentiate these
    two function types:
        void *malloc(size_t);
        void *malloc(unsigned long int);
    so you might need to do this to make make.bash pass:
    sed -i -e 's/C.malloc(C.size_t/C.malloc(C.ulong/' pkg/os/user/lookup_unix.go
    
    R=golang-dev, dave, iant, rsc
    CC=golang-dev
    https://golang.org/cl/7351044

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

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

元コミット内容

上記の「コミット」セクションに記載されている内容が元コミット内容です。

変更の背景

このコミットの主な背景は、Go言語のツールチェインがclangコンパイラと連携する際に発生していた複数の互換性問題とバグを解決することです。特に、以下の点が挙げられます。

  1. clangの厳格な診断メッセージへの対応: clangは、コンパイル時に使用されない引数に対して-Qunused-argumentsのようなオプションが指定されていても、それを未使用と判断してエラー(または警告)を出すことがあります。これは、Goのビルドシステムがclangに渡す引数の一部が、特定のコンテキストでは実際に使用されない場合に問題となっていました。
  2. libgccパスの不正確な解決: clang -print-libgcc-file-nameコマンドが、libgccライブラリの絶対パスではなく相対パスを返す場合があり、これがリンカのコマンドラインで正しく扱われない問題がありました。
  3. DWARFデバッグ情報の欠落: clangがポインタ型に対してDW_AT_byte_size属性を生成しないため、cmd/cgoがDWARFデバッグ情報を処理する際に、ポインタのサイズを正しく認識できない問題がありました。これはデバッグ情報の正確性に影響を与えます。
  4. 警告オプションの互換性: clangのバージョンによっては、-Wno-unneeded-internal-declarationのような警告抑制オプションが認識されず、それ自体が「不明な警告オプション」として警告される問題がありました。
  5. レース検出器のテストの制限: darwin(macOS)環境でclangを使用している場合、Goのレース検出器のテストが実行できないという制限がありました。これは、clangが特定の.sysoファイルをリンクできないという誤解に基づいていた可能性があります。

これらの問題は、Goのユーザーがclangを主要なCコンパイラとして使用する際に、ビルドエラーやデバッグ情報の不正確さ、テストの実行制限といった形で現れていました。コミットメッセージで言及されているUpdate #4829は、これらの問題の包括的な解決を目指すものでした(ただし、Goの公式リポジトリで直接#4829というIssueは見つかりませんでした。これは内部的なトラッキング番号か、別のプロジェクトのIssueを参照している可能性があります)。

前提知識の解説

このコミットを理解するためには、以下の技術的な概念について知っておく必要があります。

  • Go言語のcgo: cgoはGo言語の機能の一つで、GoプログラムからC言語のコードを呼び出したり、C言語のコードからGoの関数を呼び出したりするためのメカニズムを提供します。GoとCの間のデータ型の変換や、コンパイル・リンクのプロセスを管理します。
  • clang: LLVMプロジェクトの一部であるC、C++、Objective-C、Objective-C++コンパイラです。GCC(GNU Compiler Collection)の代替として広く使用されており、高速なコンパイルと優れた診断機能が特徴です。
  • GCC (GNU Compiler Collection): GNUプロジェクトによって開発されている、様々なプログラミング言語に対応したコンパイラの集合体です。C、C++、Objective-C、Fortran、Ada、Goなどの言語をサポートしています。
  • DWARF (Debugging With Attributed Record Formats): プログラムのデバッグ情報を格納するための標準的なフォーマットです。変数名、型情報、ソースコードの行番号、スタックフレーム情報などが含まれ、デバッガが実行中のプログラムの状態を解析するために使用します。
  • DW_AT_byte_size: DWARFデバッグ情報における属性の一つで、特定のデータ型がメモリ上で占めるバイトサイズを示します。ポインタ型の場合、通常はシステムのポインタサイズ(例: 32ビットシステムでは4バイト、64ビットシステムでは8バイト)が格納されます。
  • libgcc: GCCランタイムライブラリのことで、GCCが生成するコードが依存する低レベルのヘルパー関数(例: 整数演算、浮動小数点演算、例外処理など)を提供します。C言語のプログラムがこれらの関数を直接呼び出すことは稀ですが、コンパイラが生成するコードが暗黙的に依存します。
  • -Qunused-arguments: clangのコンパイラオプションの一つで、コマンドラインで指定された引数のうち、コンパイル中に使用されなかったものを警告またはエラーとして報告しないようにします。clangはデフォルトで未使用の引数に対して厳格な診断を行うため、特定のビルドシステムではこのオプションが必要になることがあります。
  • -Wno-unneeded-internal-declaration: clangの警告抑制オプションの一つで、内部的に宣言されたが使用されていない関数や変数に関する警告を抑制します。
  • -Wno-unused-function: clangの警告抑制オプションの一つで、宣言されたが使用されていない関数に関する警告を抑制します。
  • レース検出器 (Race Detector): Go言語のツールチェインに組み込まれている機能で、並行処理におけるデータ競合(data race)を検出します。データ競合は、複数のゴルーチンが同時に同じメモリ位置にアクセスし、少なくとも1つのアクセスが書き込みであり、かつアクセスが同期されていない場合に発生するバグです。レース検出器は、実行時にこれらの競合を特定し、デバッグを支援します。
  • make.bash: Go言語のソースコードをビルドするためのシェルスクリプトです。Goのツールチェイン全体をコンパイルするために使用されます。

技術的詳細

このコミットは、clangとの連携における複数の問題を、以下の6つの主要な変更点で解決しています。

  1. -Qunused-argumentsによるclang診断の回避:

    • clangは、コンパイル時に使用されないコマンドライン引数に対してエラーを出すことがあります。Goのビルドプロセスでは、特定の状況下でclangに渡される引数が未使用と判断されることがありました。
    • このコミットでは、src/cmd/cgo/gcc.gogccCmd()関数とsrc/cmd/go/build.gogccCmd()関数において、コンパイラがclangである場合に-Qunused-argumentsオプションを明示的に追加しています。これにより、clangが未使用の引数についてエラーや警告を出すのを抑制し、ビルドプロセスをスムーズに進めます。
    • また、src/cmd/dist/build.cでは、clangの場合にのみ-Qunused-argumentsを追加するように変更されています。以前はclangの場合に-g、それ以外の場合に-ggdbを追加していましたが、clangの厳格な引数チェックを回避するためにこの変更が行われました。
  2. clang -print-libgcc-file-nameの非絶対パス問題の対処:

    • clang -print-libgcc-file-nameコマンドは、libgccライブラリのパスを返しますが、これが絶対パスではない場合がありました。Goのリンカは絶対パスを期待するため、この問題はリンクエラーを引き起こす可能性がありました。
    • src/cmd/go/build.golibgcc()関数において、clangを使用しており、かつlibgccのパスが絶対パスでない場合に、そのパスをリンカに提供しないように修正されました。これにより、不正確なパスが原因で発生するリンクエラーを回避します。
  3. cmd/cgoにおけるdwarf.PtrType.Size()の修正:

    • clangは、ポインタ型に対してDWARFデバッグ情報の一部であるDW_AT_byte_size属性を生成しないことがあります。これにより、cmd/cgoがポインタのサイズを正しく取得できず、デバッグ情報が不正確になる問題がありました。
    • src/cmd/cgo/gcc.gotypeConv構造体のType()メソッドにおいて、DWARFの型がポインタ型(dwarf.PtrType)であり、かつByteSize-1(未設定)の場合に、c.ptrSize(システムのポインタサイズ)を明示的に設定するように修正されました。これにより、clangDW_AT_byte_sizeを生成しない場合でも、ポインタのサイズが正しく設定され、デバッグ情報の正確性が保たれます。
  4. -Wno-unneeded-internal-declaration警告の回避:

    • 古いバージョンのApple clang(バージョン1.7など)では、-Wno-unneeded-internal-declarationオプションが認識されず、「不明な警告オプション」として警告が出ることがありました。
    • src/cmd/cgo/gcc.gogccCmd()関数において、コンパイラがclangである場合に、-Wno-unknown-warning-optionオプションを-Wno-unneeded-internal-declarationの前に追加するように変更されました。これにより、clangが不明な警告オプションについて警告を出すのを抑制し、ビルド時のノイズを減らします。
  5. -Wno-unused-functionの追加:

    • cgoが生成するCコードには、Goから呼び出されないがC側で定義されている関数が含まれることがあり、これがclangによって「未使用の関数」として警告されることがありました。
    • src/cmd/cgo/gcc.gogccCmd()関数において、clangを使用している場合に-Wno-unused-functionオプションを追加しました。これにより、これらの警告が抑制され、ビルドログがクリーンになります。
  6. darwinにおけるレース検出器テストの有効化:

    • 以前は、darwin環境でclangを使用している場合、レース検出器のテストが実行されないように制限されていました。これは、clangが特定の.sysoファイルをリンクできないという誤解に基づいていた可能性があります。
    • src/run.bashスクリプトにおいて、レース検出器テストの実行条件から*gcc*のチェックを削除し、darwin-darwin-amd64-1darwin上のamd64cgoが有効な場合)でもテストが実行されるように変更されました。これにより、darwin環境でもレース検出器のテストが実行可能になり、より広範な環境での品質保証が可能になります。コミットメッセージには「Apple clang version 1.7 (tags/Apple/clang-77) (based on LLVM 2.9svn) works」とあり、特定のバージョンのclangでは問題なく動作することが確認されたため、この変更が可能になりました。

このコミットは、Goツールチェインとclangの間の互換性を大幅に向上させ、特にmacOSユーザーにとってのビルド体験を改善するものです。ただし、コミットメッセージの最後に記載されているように、新しいclangが生成するDWARF情報においてvoid *malloc(size_t)void *malloc(unsigned long int)のような関数型を区別できないという残存する問題も指摘されています。これは、pkg/os/user/lookup_unix.goC.malloc(C.size_tC.malloc(C.ulongに置換することで回避できるとされています。

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

このコミットでは、以下の4つのファイルが変更されています。

  1. src/cmd/cgo/gcc.go: cgoがCコンパイラ(特にclang)を呼び出す際のオプション設定と、DWARFデバッグ情報の処理に関する修正。
    • clangに渡すオプションに-Wno-unknown-warning-option, -Wno-unused-function, -Qunused-argumentsを追加。
    • ポインタ型のDWARF情報にDW_AT_byte_sizeが欠落している場合の修正ロジックを追加。
  2. src/cmd/dist/build.c: Goのビルドシステムの一部で、Cコンパイラに渡すデフォルトの引数に関する修正。
    • proto_gccargs-ggdbを追加。
    • clangの場合にのみ-Qunused-argumentsを追加するロジックに変更。以前の-gまたは-ggdbの選択ロジックを削除。
  3. src/cmd/go/build.go: goコマンドのビルドロジック、特にlibgccのパス解決とgccCmdの引数生成に関する修正。
    • libgcc()関数で、clangが非絶対パスを返す場合にlibgccパスを使用しないように修正。
    • gccCmd()関数で、clangの場合に-Qunused-argumentsを常に含めるように修正。
    • cgoLibGccFilecgoLibGccErrの扱いを改善し、エラーハンドリングをより堅牢に。
    • staticLibsの構築ロジックを修正し、cgoLibGccFileが空でない場合にのみ追加するように変更。
  4. src/run.bash: Goのテストスクリプトで、レース検出器テストの実行条件に関する修正。
    • レース検出器テストの実行条件から*gcc*のチェックを削除し、darwin上のclangでもテストが実行されるように変更。

コアとなるコードの解説

src/cmd/cgo/gcc.go

@@ -783,7 +783,13 @@ func (p *Package) gccCmd() []string {
 	if strings.Contains(p.gccName(), "clang") {
 		c = append(c,
 			"-ferror-limit=0",
+			// Apple clang version 1.7 (tags/Apple/clang-77) (based on LLVM 2.9svn)
+			// doesn't have -Wno-unneeded-internal-declaration, so we need yet another
+			// flag to disable the warning. Yes, really good diagnostics, clang.
+			"-Wno-unknown-warning-option",
 			"-Wno-unneeded-internal-declaration",
+			"-Wno-unused-function",
+			"-Qunused-arguments",
 		)
 	}

@@ -1049,6 +1055,12 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
 		return t
 	}

+	// clang won't generate DW_AT_byte_size for pointer types,
+	// so we have to fix it here.
+	if dt, ok := base(dtype).(*dwarf.PtrType); ok && dt.ByteSize == -1 {
+		dt.ByteSize = c.ptrSize
+	}
+
 	t := new(Type)
 	t.Size = dtype.Size()
 	t.Align = -1
  • gccCmd()関数: clangを使用している場合、コンパイラオプションに-Wno-unknown-warning-option(不明な警告オプションに関する警告を抑制)、-Wno-unused-function(未使用関数に関する警告を抑制)、-Qunused-arguments(未使用のコマンドライン引数に関する警告を抑制)が追加されています。これは、clangの厳格な診断機能がGoのビルドプロセスと競合するのを防ぐためです。
  • Type()メソッド: DWARFデバッグ情報を処理する際に、ポインタ型(dwarf.PtrType)のByteSize-1(未設定)である場合、c.ptrSize(システムのポインタサイズ)を代入しています。これは、clangがポインタ型に対してDW_AT_byte_size属性を生成しない問題への対処です。

src/cmd/dist/build.c

@@ -409,6 +409,7 @@ static char *proto_gccargs[] = {
 	"-Wno-comment",
 	"-Werror",
 	"-fno-common",
+	"-ggdb",
 	"-pipe",
 	"-O2",
 };
@@ -604,10 +605,10 @@ install(char *dir)
 		splitfields(&gccargs, bstr(&b));
 		for(i=0; i<nelem(proto_gccargs); i++)
 			vadd(&gccargs, proto_gccargs[i]);
-\t\tif(clang)
-\t\t\tvadd(&gccargs, "-g");
-\t\telse
-\t\t\tvadd(&gccargs, "-ggdb");
+\t\tif(clang) {
+\t\t\t// clang is too smart about unused command-line arguments
+\t\t\tvadd(&gccargs, "-Qunused-arguments");
+\t\t}
 	}

 islib = hasprefix(dir, "lib") || streq(dir, "cmd/cc") || streq(dir, "cmd/gc");
  • proto_gccargs: デフォルトのGCC引数リストに-ggdbが追加されました。これはデバッグ情報の生成をより詳細にするためのオプションです。
  • install()関数: clangを使用している場合に、以前は-gまたは-ggdbを選択していましたが、この変更によりclangの場合にのみ-Qunused-argumentsを明示的に追加するように変更されました。これは、clangが未使用の引数に対して厳格な診断を行うことを考慮したものです。

src/cmd/go/build.go

@@ -1609,6 +1609,8 @@ func gccgoCleanPkgpath(p *Package) string {
 func (b *builder) libgcc(p *Package) (string, error) {
 	var buf bytes.Buffer

+	gccCmd := b.gccCmd(p.Dir)
+
 	prev := b.print
 	if buildN {
 		// In -n mode we temporarily swap out the builder's
@@ -1619,7 +1621,7 @@ func (b *builder) libgcc(p *Package) (string, error) {
 			return fmt.Fprint(&buf, a...)
 		}
 	}
-	f, err := b.runOut(p.Dir, p.ImportPath, b.gccCmd(p.Dir), "-print-libgcc-file-name")
+	f, err := b.runOut(p.Dir, p.ImportPath, gccCmd, "-print-libgcc-file-name")
 	if err != nil {
 		return "", fmt.Errorf("gcc -print-libgcc-file-name: %v (%s)", err, f)
 	}
@@ -1629,6 +1631,13 @@ func (b *builder) libgcc(p *Package) (string, error) {
 		b.print(s)
 		return "$LIBGCC", nil
 	}
+
+	// clang might not be able to find libgcc, and in that case,
+	// it will simply return "libgcc.a", which is of no use to us.
+	if strings.Contains(gccCmd[0], "clang") && !filepath.IsAbs(string(f)) {
+		return "", nil
+	}
+
 	return strings.Trim(string(f), "\r\n"), nil
 }

@@ -1662,19 +1671,21 @@ func (b *builder) gccCmd(objdir string) []string {
 	}\n\ta = append(a, b.gccArchArgs()...)\n \t// gcc-4.5 and beyond require explicit \"-pthread\" flag\n-\t// for multithreading with pthread library, but clang whines\n-\t// about unused arguments if we pass it.\n+\t// for multithreading with pthread library.\n \tif buildContext.CgoEnabled {\n \t\tswitch goos {\n \t\tcase \"windows\":\n \t\t\ta = append(a, \"-mthreads\")\n \t\tdefault:\n-\t\t\tif !strings.Contains(a[0], \"clang\") {\n-\t\t\t\ta = append(a, \"-pthread\")\n-\t\t\t}\n+\t\t\ta = append(a, \"-pthread\")\n \t\t}\n \t}\n \n+\t// clang is too smart about command-line arguments\n+\tif strings.Contains(a[0], \"clang\") {\n+\t\ta = append(a, \"-Qunused-arguments\")\n+\t}\n+\n \t// On OS X, some of the compilers behave as if -fno-common\n \t// is always set, and the Mach-O linker in 6l/8l assumes this.\n \t// See http://golang.org/issue/3253.\n@@ -1706,6 +1717,7 @@ var cgoRe = regexp.MustCompile(`[/\\\\:]`)\n \n var (\n \tcgoLibGccFile     string\n+\tcgoLibGccErr      error\n \tcgoLibGccFileOnce sync.Once\n )\n \n@@ -1800,21 +1812,19 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo,\n \t}\n \n \tcgoLibGccFileOnce.Do(func() {\n-\t\tcgoLibGccFile, err = b.libgcc(p)\n+\t\tcgoLibGccFile, cgoLibGccErr = b.libgcc(p)\n \t})\n-\tif cgoLibGccFile == \"\" {\n-\t\tif err == nil {\n-\t\t\terr = errors.New(\"failed to get libgcc filename\")\n-\t\t}\n+\tif cgoLibGccFile == "" && cgoLibGccErr != nil {\n \t\treturn nil, nil, err\n \t}\n \n \tvar staticLibs []string\n \tif goos == \"windows\" {\n \t\t// libmingw32 and libmingwex might also use libgcc, so libgcc must come last\n-\t\tstaticLibs = []string{\"-lmingwex\", \"-lmingw32\", cgoLibGccFile}\n-\t} else {\n-\t\tstaticLibs = []string{cgoLibGccFile}\n+\t\tstaticLibs = []string{\"-lmingwex\", \"-lmingw32\"}\n+\t}\n+\tif cgoLibGccFile != "" {\n+\t\tstaticLibs = append(staticLibs, cgoLibGccFile)\n \t}\n \n \tfor _, cfile := range cfiles {\
  • libgcc()関数: clanglibgccのパスとして非絶対パス(例: "libgcc.a")を返す場合、そのパスを無視するように変更されました。これは、clanglibgccを見つけられない場合にこのような挙動を示すため、不正確なパスをリンカに渡すのを防ぐためです。
  • gccCmd()関数: cgoが有効な場合、-pthreadオプションが常にgccCmdに追加されるようになりました。以前はclangでない場合にのみ追加されていましたが、clangが未使用引数について警告を出す問題が-Qunused-argumentsで解決されたため、常に含めることができるようになりました。また、clangを使用している場合に-Qunused-argumentsが常にgccCmdに追加されるようになりました。
  • cgo()関数: cgoLibGccFilecgoLibGccErrという新しい変数が導入され、libgccのファイル名取得とそのエラーをより適切に管理するようになりました。libgccのファイル名が空で、かつエラーが発生している場合にのみエラーを返すようにロジックが変更されました。また、staticLibsの構築ロジックが修正され、cgoLibGccFileが空でない場合にのみstaticLibsに追加されるようになりました。これにより、libgccが見つからない場合でも、他の静的ライブラリのリンクが妨げられないようになります。

src/run.bash

@@ -48,10 +48,8 @@ go test sync -short -timeout=120s -cpu=10

 # Race detector only supported on Linux and OS X,
 # and only on amd64, and only when cgo is enabled.
-# Also, clang can't seem to link the .syso files, so only
-# run if we're using gcc.
-case "$GOHOSTOS-$GOOS-$GOARCH-$CGO_ENABLED-${CC:-gcc}" in
-linux-linux-amd64-1-*gcc* | darwin-darwin-amd64-1-*gcc*)
+case "$GOHOSTOS-$GOOS-$GOARCH-$CGO_ENABLED" in
+linux-linux-amd64-1 | darwin-darwin-amd64-1)
 	echo
 	echo '# Testing race detector.'
 	go test -race -i flag
  • レース検出器テストの実行条件が緩和されました。以前はCC環境変数が*gcc*を含む場合にのみ実行されていましたが、この変更によりlinux-linux-amd64-1またはdarwin-darwin-amd64-1(それぞれLinuxまたはmacOS上のamd64アーキテクチャでcgoが有効な場合)であれば、コンパイラがclangであってもレース検出器テストが実行されるようになりました。これは、clang.sysoファイルをリンクできないという以前の懸念が解消されたためです。

関連リンク

参考にした情報源リンク