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

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

このコミットは、Go言語のcgoツールにおけるシンボルエクスポートのメカニズムを改善し、より柔軟なリンカフラグの指定を可能にするものです。具体的には、既存のcgo_exportプラグマを静的リンクと動的リンクのシナリオに合わせてcgo_export_staticcgo_export_dynamicに分割し、さらにcgo_ldflagプラグマを導入して、Goのソースコードから直接リンカフラグを指定できるようにしています。これにより、Cgoを利用したGoとCの連携が、より細かく制御できるようになります。

コミット

commit fb59aed60b64319ad6fb1d6e6f18e7b1d96aaf77
Author: Russ Cox <rsc@golang.org>
Date:   Wed Mar 6 16:57:14 2013 -0500

    cmd/cgo: split cgo_export into cgo_export_static and cgo_export_dynamic
    
    Also emit cgo_ldflag pragmas.
    
    R=golang-dev, remyoudompheng, iant
    CC=golang-dev
    https://golang.org/cl/7530043

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

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

元コミット内容

このコミットは、cgoツールがGoとCのコードを連携させる際に使用するディレクティブの挙動を変更・拡張しています。

主な変更点は以下の通りです。

  1. cgo_exportプラグマの分割:
    • これまでの#pragma cgo_exportは、静的リンクと動的リンクの両方でGoのシンボルをCコードにエクスポートするために使用されていました。
    • このコミットにより、#pragma cgo_export_static#pragma cgo_export_dynamicに明確に分割されました。
    • #pragma dynexport#pragma cgo_export_dynamicのエイリアスとして残されています。
  2. cgo_ldflagプラグマの導入:
    • #pragma cgo_ldflag "arg"という新しいプラグマが導入されました。これにより、Goのソースコード内で直接リンカフラグを指定できるようになります。これは特に外部リンカ(通常はgcc)を使用する際に役立ちます。
  3. CgoFlagsのデータ構造変更:
    • src/cmd/cgo/main.goにおいて、Package構造体のCgoFlagsフィールドの型がmap[string]stringからmap[string][]stringに変更されました。これにより、同じフラグ名に対して複数の引数をリストとして保持できるようになり、特にLDFLAGSのような複数の引数を取るフラグの扱いが改善されます。
  4. リンカでの処理の更新:
    • src/cmd/ld/go.cにおいて、リンカがcgo_export_dynamiccgo_ldflagディレクティブを適切に解釈し、処理するように更新されました。

これらの変更により、Cgoを利用したGoプログラムのビルドプロセスにおいて、シンボルエクスポートとリンカオプションの指定がより細かく、かつ意図的に制御できるようになります。

変更の背景

このコミットが行われた背景には、GoプログラムとCライブラリを連携させるcgoの機能性、特にシンボルエクスポートとリンカオプションの指定における柔軟性の向上が求められていたことがあります。

  1. 静的リンクと動的リンクの区別:

    • Goプログラムは、デフォルトで静的リンクを志向しますが、Cgoを使用する場合、Cライブラリとの連携においては動的リンクが必要となるケースも少なくありません。
    • 従来のcgo_exportプラグマは、静的・動的リンクの区別なくシンボルをエクスポートする機能を提供していましたが、これにより、特定のリンクモードでのみシンボルをエクスポートしたいという、より高度な要件に対応できませんでした。例えば、共有ライブラリとしてビルドされるGoコードからCコードにシンボルをエクスポートする場合と、完全に静的にリンクされる実行ファイルからエクスポートする場合では、その挙動や意図が異なることがあります。この曖昧さを解消し、開発者が明示的にどちらのリンクモードでシンボルをエクスポートするかを制御できるようにするために、cgo_export_staticcgo_export_dynamicへの分割が必要とされました。
  2. リンカフラグの直接指定:

    • cgoは、Cコードをコンパイル・リンクするために、内部的にCコンパイラ(通常はgcc)を呼び出します。この際、特定のライブラリをリンクしたり、リンカの挙動を調整したりするために、リンカフラグ(LDFLAGS)を渡す必要があります。
    • これまでは、#cgo LDFLAGS: -L/path/to/lib -lmylibのように、#cgoディレクティブを通じてリンカフラグを指定していました。しかし、これはGoのビルドシステムが_cgo_flagsファイルに書き出し、それを外部のビルドシステム(Makeなど)が読み取ってgccに渡すという間接的な方法でした。
    • #pragma cgo_ldflagの導入は、Goのソースコードから直接リンカにフラグを渡す、より直接的なメカニズムを提供します。これにより、ビルドスクリプトの複雑さを軽減し、GoコードとCコードのリンカ設定をより密接に管理できるようになります。これは、特に複雑なCライブラリに依存するプロジェクトや、特定のリンカ機能(例: バージョンシンボル、セクション配置)を利用したい場合に有用です。

これらの変更は、cgoの柔軟性と表現力を高め、GoとCのハイブリッドアプリケーション開発におけるより複雑なシナリオに対応するための基盤を強化することを目的としています。

前提知識の解説

このコミットの理解を深めるためには、以下の概念について基本的な知識が必要です。

  1. Go言語のcgo:

    • cgoはGo言語に組み込まれているツールで、GoプログラムからC言語のコードを呼び出したり、逆にC言語のコードからGo言語の関数を呼び出したりするためのメカニズムを提供します。これはForeign Function Interface (FFI) の一種です。
    • Goのソースファイル内にimport "C"と記述することでcgoが有効になり、Cの関数をGoから呼び出したり、Goの関数をCから呼び出せるようにエクスポートしたりするための特別なコメント(#cgoディレクティブや#pragmaディレクティブ)を使用します。
    • cgoは、Goのビルドプロセス中にCコードをコンパイルし、Goのオブジェクトファイルとリンクするための橋渡し役を担います。
  2. 静的リンクと動的リンク:

    • 静的リンク (Static Linking): プログラムが依存するすべてのライブラリのコードが、最終的な実行ファイルに直接組み込まれる方式です。
      • 利点: 実行ファイルが自己完結型になり、他のシステムに配布する際に依存ライブラリの有無を気にする必要がありません。実行時のパフォーマンスがわずかに向上する可能性があります。
      • 欠点: 実行ファイルのサイズが大きくなる傾向があります。複数のプログラムが同じライブラリを使用する場合でも、それぞれの実行ファイルにライブラリのコピーが含まれるため、ディスクスペースの無駄が生じます。ライブラリの更新があった場合、そのライブラリに依存するすべてのプログラムを再コンパイル・再リンクする必要があります。
    • 動的リンク (Dynamic Linking): プログラムが依存するライブラリのコードが、最終的な実行ファイルには組み込まれず、実行時に共有ライブラリ(WindowsではDLL、Linuxでは.so、macOSでは.dylib)としてロードされる方式です。
      • 利点: 実行ファイルのサイズが小さくなります。複数のプログラムが同じ共有ライブラリを使用する場合、メモリ上でライブラリのコードを共有できるため、メモリ効率が良くなります。ライブラリの更新があった場合、プログラムを再コンパイル・再リンクすることなく、共有ライブラリを置き換えるだけで対応できます。
      • 欠点: 実行時に依存ライブラリが存在しないとプログラムが起動できません("DLL Hell"や"dependency hell"と呼ばれる問題)。実行時のパフォーマンスがわずかに低下する可能性があります。
  3. シンボルエクスポート:

    • プログラムやライブラリが、外部のコードからアクセスできるように、その内部の関数や変数の名前(シンボル)を公開することです。
    • Cgoにおいては、Goで定義された関数や変数をCコードから呼び出せるようにするために、これらのGoシンボルをCのリンカが認識できる形式でエクスポートする必要があります。
  4. プラグマ (Pragma):

    • コンパイラやリンカに対する特別な指示を与えるためのディレクティブです。C/C++では#pragmaキーワードを使用します。
    • cgoでは、Goのソースコード内に#pragmaディレクティブを記述することで、cgoツールに対して特定の処理(例: シンボルのエクスポート、リンカフラグの追加)を指示します。
  5. リンカフラグ (Linker Flags / LDFLAGS):

    • リンカに渡されるコマンドライン引数で、リンク処理の挙動を制御します。
    • 例:
      • -L/path/to/lib: ライブラリを検索するディレクトリを追加します。
      • -lfoo: libfoo.aまたはlibfoo.so(または対応する動的ライブラリ)という名前のライブラリをリンクします。
      • -Wl,--version-script=version.map: 特定のリンカスクリプトを使用します。
    • cgoを使用するGoプログラムでは、Cコードが依存する外部ライブラリをリンクするためにこれらのフラグが必要となることがあります。

これらの概念を理解することで、このコミットがGoのビルドシステム、特にCgoとの連携においてどのような課題を解決し、どのような機能強化をもたらしたのかを深く把握することができます。

技術的詳細

このコミットは、Goのcgoツールチェーンにおけるシンボルエクスポートとリンカフラグ処理の内部実装に深く関わる変更を含んでいます。

  1. #pragma cgo_exportの分割とsrc/cmd/cc/dpchk.cの変更:

    • src/cmd/cc/dpchk.cは、Goのコンパイラ(cmd/cc)の一部であり、Cgo関連の#pragmaディレクティブを解析する役割を担っています。
    • 変更前は、strcmp(verb, "cgo_export") == 0 || strcmp(verb, "dynexport") == 0という条件でcgo_exportdynexportを同じように処理していました。
    • 変更後、dynexportcgo_export_dynamicに内部的に変換され、cgo_export_staticcgo_export_dynamicが明示的に区別されるようになりました。
    • fmtprint(&pragcgobuf, "%s %q\\n", verb, local->name);のように、verbcgo_export_staticまたはcgo_export_dynamic)が直接pragcgobuf_cgo_export.cなどのCgoが生成する中間ファイルに書き込まれるバッファ)に書き込まれるようになりました。これにより、リンカがどの種類のエクスポートであるかを正確に識別できるようになります。
    • エラーメッセージもyyerror("usage: #pragma %s local [remote]", verb);のように、使用されたプラグマ名を含むように改善されています。
  2. #pragma cgo_ldflagの導入とsrc/cmd/cc/dpchk.csrc/cmd/ld/go.cの変更:

    • src/cmd/cc/dpchk.cに、新しい#pragma cgo_ldflagの解析ロジックが追加されました。これは引用符で囲まれた文字列引数(リンカフラグ)を受け取り、fmtprint(&pragcgobuf, "cgo_ldflag %q\\n", p);として中間ファイルに書き出します。
    • src/cmd/ld/go.cはGoのリンカ(cmd/ld)のCgo関連の処理を担当しています。このファイルに、cgo_ldflagディレクティブを読み取り、その引数をリンカの内部的なldflagリストに追加するロジックが追加されました。
    • ldflag = realloc(ldflag, (nldflag+32)*sizeof ldflag[0]);のように、動的にリンカフラグの配列を拡張して格納しています。これにより、Goのソースコード内で指定されたリンカフラグが、最終的なリンクコマンドに確実に含まれるようになります。
  3. CgoFlagsの型変更とsrc/cmd/cgo/main.gosrc/cmd/cgo/out.goの変更:

    • src/cmd/cgo/main.goPackage構造体において、CgoFlagsフィールドの型がmap[string]stringからmap[string][]stringに変更されました。これは、#cgoディレクティブで指定されるCFLAGSLDFLAGSが、実際には複数の引数を持つ可能性があるためです。例えば、-L/path/to/lib -lmylibは2つの異なる引数として扱われるべきです。
    • src/cmd/cgo/gcc.goaddToFlag関数も、この新しい型に合わせてp.CgoFlags[flag] = append(p.CgoFlags[flag], args...)のように、引数をリストに直接追加するように変更されました。
    • src/cmd/cgo/out.goでは、_cgo_flagsファイルへの書き出しロジックが更新されました。fmt.Fprintf(fflg, "_CGO_%s=%s\\n", k, strings.Join(v, " "))のように、CgoFlagsマップの値が文字列のリストになったため、strings.Joinを使ってスペース区切りの単一文字列として書き出されます。
    • さらに重要な変更として、src/cmd/cgo/out.goLDFLAGS処理が追加されました。if k == "LDFLAGS"のブロック内で、LDFLAGSの各引数に対して#pragma cgo_ldflag %q\\nfc(Cgoが生成するCソースファイル)に書き出すようになりました。これは、#cgo LDFLAGSで指定されたフラグが、最終的に#pragma cgo_ldflagとしてCgoの中間Cファイルに埋め込まれ、リンカによって処理されるという新しいフローを確立します。
  4. リンカにおけるcgo_export_dynamicの処理:

    • src/cmd/ld/go.cloadcgo関数内で、cgo_export_dynamicディレクティブの処理が更新されました。
    • s->dynimpname = remote;s->dynexport = 1;は、シンボルが動的にエクスポートされることを示します。
    • dynexp配列は、動的にエクスポートされるシンボルのリストを保持します。この配列への追加ロジックも、競合するエクスポートディレクティブがないかを確認するように強化されています。

これらの変更は、GoのビルドシステムがCgoを介してCコードと連携する際の、シンボル管理とリンカオプション指定の柔軟性と堅牢性を大幅に向上させるものです。特に、静的・動的リンクの区別を明確にし、リンカフラグをGoソースから直接制御できるようになった点は、複雑なC/Goハイブリッドプロジェクトの開発において大きなメリットとなります。

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

このコミットにおける主要なコード変更は、以下のファイルに集中しています。

  1. src/cmd/cc/dpchk.c:

    • pragcgo関数内で、cgo_exportdynexportの処理がcgo_export_staticcgo_export_dynamicに分割されました。
    • 新しいcgo_ldflagプラグマの解析ロジックが追加されました。
    --- a/src/cmd/cc/dpchk.c
    +++ b/src/cmd/cc/dpchk.c
    @@ -692,22 +692,24 @@ pragcgo(char *verb)
     		goto out;
     	}	
     	
    -	if(strcmp(verb, "cgo_export") == 0 || strcmp(verb, "dynexport") == 0) {
    +	if(strcmp(verb, "dynexport") == 0)
    +		verb = "cgo_export_dynamic";
    +	if(strcmp(verb, "cgo_export_static") == 0 || strcmp(verb, "cgo_export_dynamic") == 0) {
     		local = getimpsym();
     		if(local == nil)
     			goto err2;
     		if(!more()) {
    -			fmtprint(&pragcgobuf, "cgo_export %q\n", local->name);
    +			fmtprint(&pragcgobuf, "%s %q\n", verb, local->name);
     			goto out;
     		}
     		remote = getimpsym();
     		if(remote == nil)
     			goto err2;
    -		fmtprint(&pragcgobuf, "cgo_export %q %q\n", local->name, remote->name);
    +		fmtprint(&pragcgobuf, "%s %q %q\n", verb, local->name, remote->name);
     		goto out;
     	
     err2:
    -		yyerror("usage: #pragma cgo_export local [remote]");
    +		yyerror("usage: #pragma %s local [remote]", verb);
     		goto out;
     	}
     	
    @@ -749,6 +751,18 @@ pragcgo(char *verb)
     		goto out;
     	}
     	
    +	if(strcmp(verb, "cgo_ldflag") == 0) {
    +		p = getquoted();
    +		if(p == nil)
    +			goto err5;
    +		fmtprint(&pragcgobuf, "cgo_ldflag %q\n", p);
    +		goto out;
    +
    +	err5:
    +		yyerror("usage: #pragma cgo_ldflag \"arg\"");
    +		goto out;
    +	}
    +	
     out:
     	while(getnsc() != '\n')
     		;
    
  2. src/cmd/cgo/doc.go:

    • cgo_exportのドキュメントがcgo_export_dynamicに更新され、cgo_export_staticの新しい説明が追加されました。
    • cgo_ldflagのドキュメントが追加されました。
  3. src/cmd/cgo/main.go:

    • Package構造体のCgoFlagsフィールドの型がmap[string]stringからmap[string][]stringに変更されました。
    --- a/src/cmd/cgo/main.go
    +++ b/src/cmd/cgo/main.go
    @@ -33,7 +33,7 @@ type Package struct {
     	PtrSize     int64
     	IntSize     int64
     	GccOptions  []string
    -	CgoFlags    map[string]string // #cgo flags (CFLAGS, LDFLAGS)
    +	CgoFlags    map[string][]string // #cgo flags (CFLAGS, LDFLAGS)
     	Written     map[string]bool
     	Name        map[string]*Name // accumulated Name from Files
     	ExpFunc     []*ExpFunc       // accumulated ExpFunc from Files
    @@ -312,7 +312,7 @@ func newPackage(args []string) *Package {
     		PtrSize:    ptrSize,
     		IntSize:    intSize,
     		GccOptions: gccOptions,
    -		CgoFlags:   make(map[string]string),
    +		CgoFlags:   make(map[string][]string),
     		Written:    make(map[string]bool),
     	}
     	return p
    
  4. src/cmd/cgo/out.go:

    • _cgo_flagsファイルへの書き出しで、CgoFlagsの値がstrings.Joinで結合されるようになりました。
    • LDFLAGSに対して、各引数を#pragma cgo_ldflagとしてCgoが生成するCソースファイルに書き出すロジックが追加されました。
    • cgo_exportの代わりにcgo_export_dynamicが使用されるようになりました。
    • cgo_export_staticの新しいプラグマが追加されました。
    --- a/src/cmd/cgo/out.go
    +++ b/src/cmd/cgo/out.go
    @@ -31,7 +31,12 @@ func (p *Package) writeDefs() {
     
     	fflg := creat(*objDir + "_cgo_flags")
     	for k, v := range p.CgoFlags {
    -		fmt.Fprintf(fflg, "_CGO_%s=%s\\n", k, v)
    +		fmt.Fprintf(fflg, "_CGO_%s=%s\\n", k, strings.Join(v, " "))
    +		if k == "LDFLAGS" {
    +			for _, arg := range v {
    +				fmt.Fprintf(fc, "#pragma cgo_ldflag %q\\n", arg)
    +			}
    +		}
     	}
     	fflg.Close()
     
    @@ -100,6 +105,7 @@ func (p *Package) writeDefs() {
     			fmt.Fprintf(fm, "extern char %s[];\\n", n.C)
     			fmt.Fprintf(fm, "void *_cgohack_%s = %s;\\n\\n", n.C, n.C)
     
    +			fmt.Fprintf(fc, "#pragma cgo_import_static %s\\n", n.C)
     			fmt.Fprintf(fc, "extern byte *%s;\\n", n.C)
     
     			cVars[n.C] = true
    @@ -651,8 +657,9 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) {
     		if fn.Recv != nil {
     			goname = "_cgoexpwrap" + cPrefix + "_" + fn.Recv.List[0].Names[0].Name + "_" + goname
     		}
    -		fmt.Fprintf(fc, "#pragma cgo_export %s\\n", goname)
    +		fmt.Fprintf(fc, "#pragma cgo_export_dynamic %s\\n", goname)
     		fmt.Fprintf(fc, "extern void ·%s();\\n\\n", goname)
    +		fmt.Fprintf(fc, "#pragma cgo_export_static _cgoexp%s_%s\\n", cPrefix, exp.ExpName)
     		fmt.Fprintf(fc, "#pragma textflag 7\\n") // no split stack, so no use of m or g
     		fmt.Fprintf(fc, "void\\n")
     		fmt.Fprintf(fc, "_cgoexp%s_%s(void *a, int32 n)\\n", cPrefix, exp.ExpName)
    
  5. src/cmd/ld/go.c:

    • loadcgo関数内で、cgo_exportの代わりにcgo_export_dynamicを処理するように変更されました。
    • cgo_ldflagディレクティブを解析し、リンカの内部的なldflagリストに追加するロジックが追加されました。
    --- a/src/cmd/ld/go.c
    +++ b/src/cmd/ld/go.c
    @@ -487,7 +487,9 @@ loadcgo(char *file, char *pkg, char *p, int n)
     			continue;
     		}
     
    -		if(strcmp(f[0], "cgo_export") == 0) {
    +		// TODO: cgo_export_static
    +
    +		if(strcmp(f[0], "cgo_export_dynamic") == 0) {
     			if(nf < 2 || nf > 3)
     				goto err;
     			local = f[1];
    @@ -501,13 +503,17 @@ loadcgo(char *file, char *pkg, char *p, int n)
     			if(fprint(2, "%s: symbol is both imported and exported: %s\\n", argv0, local), nerrors++, 0)
     				;
     			}
    -			s->dynimpname = remote;
     			s->dynexport = 1;
    -
    -			if(ndynexp%32 == 0)
    -				dynexp = realloc(dynexp, (ndynexp+32)*sizeof dynexp[0]);
    -			dynexp[ndynexp++] = s;
    -
    +			if(s->dynimpname == nil) {
    +				s->dynimpname = remote;
    +				if(ndynexp%32 == 0)
    +					dynexp = realloc(dynexp, (ndynexp+32)*sizeof dynexp[0]);
    +				dynexp[ndynexp++] = s;
    +			} else if(strcmp(s->dynimpname, remote) != 0) {
    +				fprint(2, "%s: conflicting cgo_export directives: %s as %s and %s\\n", argv0, s->name, s->dynimpname, remote);
    +				nerrors++;
    +				return;
    +			}
     			if(local != f[1])
     				free(local);
     			continue;
    @@ -528,6 +534,15 @@ loadcgo(char *file, char *pkg, char *p, int n)
     			}
     			continue;
     		}
    +		
    +		if(strcmp(f[0], "cgo_ldflag") == 0) {
    +			if(nf != 2)
    +				goto err;
    +			if(nldflag%32 == 0)
    +				ldflag = realloc(ldflag, (nldflag+32)*sizeof ldflag[0]);
    +			ldflag[nldflag++] = strdup(f[1]);
    +			continue;
    +		}
     	}
     	free(p0);
     	return;
    
  6. src/cmd/ld/lib.h:

    • リンカの内部変数としてnldflagldflagが追加されました。
    --- a/src/cmd/ld/lib.h
    +++ b/src/cmd/ld/lib.h
    @@ -138,6 +138,8 @@ EXTERN	char*	outfile;
     EXTERN	int32	nsymbol;
     EXTERN	char*	thestring;
     EXTERN	int	ndynexp;
    +EXTERN	int	nldflag;
    +EXTERN	char**	ldflag;
     EXTERN	int	havedynamic;
     EXTERN	int	iscgo;
     EXTERN	int	isobj;
    

これらの変更は、cgoのフロントエンド(cmd/cc)、中間処理(cmd/cgo)、そしてバックエンド(cmd/ld)にわたる一貫した機能強化を示しています。

コアとなるコードの解説

このコミットのコアとなる変更は、cgoがGoとCの間のインターフェースをどのように生成し、リンカにどのように指示を伝えるかというメカニズムにあります。

  1. #pragma cgo_exportの分割 (src/cmd/cc/dpchk.c):

    • src/cmd/cc/dpchk.cpragcgo関数は、Goのソースコード内に埋め込まれた#pragmaディレクティブを解析する部分です。
    • 以前はcgo_exportdynexportが同じロジックで処理されていましたが、この変更により、dynexportは内部的にcgo_export_dynamicとして扱われるようになります。
    • そして、cgo_export_staticcgo_export_dynamicが明示的に区別され、それぞれのプラグマ名がそのままpragcgobuf(Cgoが生成するCファイルに書き込まれるバッファ)に書き込まれます。
    • これにより、後続のツール(特にリンカ)が、このシンボルが静的リンク時にエクスポートされるべきか、動的リンク時にエクスポートされるべきかを正確に判断できるようになります。これは、Goの関数をCの共有ライブラリから呼び出す場合と、Goの実行ファイルに静的にリンクされたCコードから呼び出す場合で、リンカの挙動を最適化するために重要です。
  2. #pragma cgo_ldflagの処理フロー (src/cmd/cc/dpchk.c, src/cmd/cgo/out.go, src/cmd/ld/go.c):

    • src/cmd/cc/dpchk.c: Goソース内の#pragma cgo_ldflag "arg"を解析し、cgo_ldflag "arg"という形式で中間ファイルに書き出します。
    • src/cmd/cgo/out.go: ここが重要な変更点です。#cgo LDFLAGS: ...というGoのビルドディレクティブで指定されたリンカフラグは、Package.CgoFlags["LDFLAGS"]に文字列のリストとして格納されます。このコミットでは、out.goがこのLDFLAGSリストの各要素を読み取り、それぞれを#pragma cgo_ldflag "..."という形式でCgoが生成するCソースファイル(_cgo_main.cなど)に書き出すようになりました。
      • つまり、#cgo LDFLAGS: -L/usr/local/lib -lmylibと書くと、Cgoは内部的に#pragma cgo_ldflag "-L/usr/local/lib"#pragma cgo_ldflag "-lmylib"という2つのプラグマを生成するわけです。
    • src/cmd/ld/go.c: Goのリンカは、Cgoが生成したCソースファイルをコンパイルした結果のオブジェクトファイルを読み込みます。このオブジェクトファイルには、#pragma cgo_ldflagディレクティブが埋め込まれています。リンカはこれらのディレクティブを解析し、指定されたリンカフラグを自身の内部的なリンカフラグリスト(ldflag配列)に追加します。最終的に、リンカが外部のCリンカ(gccなど)を呼び出す際に、このldflagリストの内容がコマンドライン引数として渡されます。
    • このフローにより、Goのソースコード内で指定されたリンカフラグが、Goのビルドシステムを介して最終的なリンクコマンドに直接、かつ確実に反映されるようになります。これは、特にクロスコンパイル環境や、特定のリンカ機能に依存する複雑なビルド設定において、非常に有用な機能です。
  3. CgoFlagsの型変更 (src/cmd/cgo/main.go):

    • CgoFlagsmap[string]stringからmap[string][]stringに変更されたことで、#cgoディレクティブで指定されるフラグ(例: CFLAGS, LDFLAGS)が、単一の文字列ではなく、複数の文字列(引数)のリストとして扱えるようになりました。
    • これにより、-L/path -lfooのような複数の引数を持つフラグが、["-L/path", "-lfoo"]のように正確に表現・処理できるようになり、ビルドシステムの堅牢性が向上します。

これらの変更は、GoのビルドシステムがCgoを介してCコードと連携する際の、シンボル管理とリンカオプション指定の柔軟性と堅牢性を大幅に向上させるものです。特に、静的・動的リンクの区別を明確にし、リンカフラグをGoソースから直接制御できるようになった点は、複雑なC/Goハイブリッドプロジェクトの開発において大きなメリットとなります。

関連リンク

  • Go言語のcgoに関する公式ドキュメント: https://pkg.go.dev/cmd/cgo
  • Go言語のリンカに関する情報(Goのソースコード内): src/cmd/ld/ ディレクトリ
  • Go言語のコンパイラに関する情報(Goのソースコード内): src/cmd/cc/ ディレクトリ

参考にした情報源リンク

  • Go言語のソースコード: https://github.com/golang/go
  • このコミットの変更リスト (Gerrit Change-ID): https://golang.org/cl/7530043 (現在はGitHubのコミットページにリダイレクトされます)
  • 静的リンクと動的リンクに関する一般的な情報源(例: Wikipedia, プログラミング関連のブログや書籍)
  • C言語の#pragmaディレクティブに関する一般的な情報源(例: C言語の標準仕様、コンパイラのドキュメント)
  • リンカの動作とリンカフラグに関する一般的な情報源(例: GNU Binutilsのドキュメント、OSのリンカマニュアル)