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

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

このコミットは、Go言語のcgoツールにおけるGoStringNおよびGoBytes関数の引数型をintgoからint32(またはC言語側ではintint32_t)に変更するものです。これにより、ビルドの問題が修正されました。

コミット

commit 598de87b0fa26dd13964c035e561f673d058baf5
Author: Ian Lance Taylor <iant@golang.org>
Date:   Thu May 23 23:19:47 2013 -0700

    cmd/cgo: change GoStringN and GoBytes from intgo to int32
    
    Fixes build.
    
    R=golang-dev
    CC=golang-dev
    https://golang.org/cl/9667047

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

https://github.com/golang/go/commit/598de87b0fa26dd13964c035e561f673d058baf5

元コミット内容

cmd/cgo: change GoStringN and GoBytes from intgo to int32

このコミットの目的は、cgoコマンドが生成するコードにおいて、GoStringN関数とGoBytes関数の引数型をintgoからint32に変更することです。これにより、発生していたビルドエラーが解消されます。

変更の背景

Go言語とC言語を連携させるcgoツールにおいて、Goの型とCの型の間で適切なマッピングが行われる必要があります。特に、Goのint型はプラットフォームによってサイズが異なる(32ビットまたは64ビット)ため、C言語側で固定サイズの整数型(例: int32_t)と連携する際に問題が発生することがあります。

このコミットが行われた2013年当時、cgoが生成するCコード内で使用されるintgo型が、特定の環境やコンパイラ設定において、Goのint型とCのint型との間でサイズや表現の不一致を引き起こし、ビルドエラーを発生させていたと考えられます。特に、Goのintが32ビット幅であると仮定されているCコードと、Goのintが64ビット幅である環境との間で不整合が生じた可能性があります。

GoStringNGoBytesは、Cのポインタと長さをGoの文字列(string)やバイトスライス([]byte)に変換するための内部的なヘルパー関数です。これらの関数が受け取る長さ引数の型が正しくない場合、メモリの読み取り範囲の誤りや、型変換エラーによるビルド失敗に直結します。

前提知識の解説

  • cgo: Go言語のプログラムからC言語のコードを呼び出すためのツールです。Goのコード内にCのコードを直接記述し、import "C"とすることで、GoとCの間で関数呼び出しやデータ交換を可能にします。cgoは、GoとCの間のインターフェースとなるCヘッダーファイルやGoソースファイルを生成します。
  • Goのint: Go言語のint型は、その実行環境のCPUアーキテクチャに依存します。32ビットシステムでは32ビット幅、64ビットシステムでは64ビット幅となります。これは、C言語のint型が通常32ビット幅であることと異なる場合があり、cgoで問題を引き起こす可能性があります。
  • intgo: cgoの内部で定義される型で、Goのint型に対応するCの型として使われます。通常はtypedef int intgo;のように定義されますが、これはCのintがGoのintと同じサイズであるという仮定に基づいています。しかし、この仮定が常に正しいとは限りません。
  • int32型 (Go): Go言語における固定幅の32ビット符号付き整数型です。
  • int32_t型 (C): C言語における固定幅の32ビット符号付き整数型です。stdint.hヘッダーで定義されており、プラットフォームに依存しない固定幅の整数型が必要な場合に使用されます。
  • GoStringNGoBytes: これらはcgoの内部で使われる関数で、C言語のポインタと長さをGoのstring型や[]byte型に変換するために利用されます。例えば、Cの文字列char*と長さintを受け取り、Goのstringを生成する際にGoStringNが使われます。

技術的詳細

このコミットの核心は、cgoが生成するCコードとGoランタイムの間の型の一貫性を確保することにあります。

src/cmd/cgo/out.goは、cgoツールがCコードを生成する際のテンプレートやロジックを含んでいます。このファイル内で、Goの内部関数やCのヘルパー関数のシグネチャが定義されています。

変更前は、GoStringNGoBytesの長さ引数にintgoが使用されていました。

_GoString_ GoStringN(char *p, intgo l);
_GoBytes_ GoBytes(void *p, intgo n);

そして、GoランタイムとのインターフェースとなるC関数でもintgoが使われていました。

void ·_Cfunc_GoStringN(int8 *p, intgo l, String s)
void ·_Cfunc_GoBytes(int8 *p, intgo l, Slice s)

さらに、Goの内部でCの関数を呼び出す部分でもintgoが使われていました。

struct __go_string GoStringN(char *p, intgo n)
Slice GoBytes(char *p, intgo n)

このintgoが、特定のビルド環境(例えば、Goのintが64ビット幅である環境で、Cのintが32ビット幅である場合など)で問題を引き起こしました。GoのintとCのintのサイズが異なる場合、intgoがGoのintのサイズに合わせられず、Cの関数呼び出し規約や型変換で不整合が生じ、コンパイルエラーやリンクエラーが発生したと考えられます。

このコミットでは、長さ引数の型をより明示的で固定サイズのint32(C側ではintまたはint32_t)に変更することで、この問題を解決しています。

  • Cの関数宣言では、intgoをCの標準的なintに変更。これは通常32ビット幅を意味します。
    _GoString_ GoStringN(char *p, int l);
    _GoBytes_ GoBytes(void *p, int n);
    
  • GoランタイムとのインターフェースとなるC関数では、intgoint32に変更。これはGoのint32型に対応します。
    void ·_Cfunc_GoStringN(int8 *p, int32 l, String s)
    void ·_Cfunc_GoBytes(int8 *p, int32 l, Slice s)
    
  • Goの内部でCの関数を呼び出す部分では、intgoをCの固定幅型int32_tに変更。これにより、Goのint32とCのint32_tが確実に一致します。
    struct __go_string GoStringN(char *p, int32_t n)
    Slice GoBytes(char *p, int32_t n)
    

この変更により、Goのintのサイズが32ビットであろうと64ビットであろうと、GoStringNGoBytesが受け取る長さ引数は常に32ビット幅として扱われるようになり、型変換の不整合が解消され、ビルドが成功するようになりました。これは、cgoがGoとCの間のポータビリティと堅牢性を高めるための重要な修正です。

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

src/cmd/cgo/out.go ファイルにおいて、以下の変更が行われました。

  1. GoStringNGoBytesのC言語側の関数プロトタイプ宣言で、長さ引数の型がintgoからintに変更されました。

    --- a/src/cmd/cgo/out.go
    +++ b/src/cmd/cgo/out.go
    @@ -1040,8 +1040,8 @@ typedef int intgo;
     typedef struct { char *p; intgo n; } _GoString_;
     typedef struct { char *p; intgo n; intgo c; } _GoBytes_;
     _GoString_ GoString(char *p);
    -_GoString_ GoStringN(char *p, intgo l);
    -_GoBytes_ GoBytes(void *p, intgo n);
    +_GoString_ GoStringN(char *p, int l);
    +_GoBytes_ GoBytes(void *p, int n);
     char *CString(_GoString_);
     `
    
  2. Goランタイムから呼び出されるC関数·_Cfunc_GoStringN·_Cfunc_GoBytesの引数型がintgoからint32に変更されました。

    --- a/src/cmd/cgo/out.go
    +++ b/src/cmd/cgo/out.go
    @@ -1059,14 +1059,14 @@ void
     }\n
     void
    -·_Cfunc_GoStringN(int8 *p, intgo l, String s)\n
    +·_Cfunc_GoStringN(int8 *p, int32 l, String s)\n
     {\n
     	s = runtime·gostringn((byte*)p, l);\n
     	FLUSH(&s);\n
     }\n
     \n     void
    -·_Cfunc_GoBytes(int8 *p, intgo l, Slice s)\n
    +·_Cfunc_GoBytes(int8 *p, int32 l, Slice s)\n
     {\n
     	s = runtime·gobytes((byte*)p, l);\n
     	FLUSH(&s);\
    
  3. Goの内部でCの関数を呼び出す部分(GoStringNGoBytesの実装)で、長さ引数の型がintgoからint32_tに変更されました。

    --- a/src/cmd/cgo/out.go
    +++ b/src/cmd/cgo/out.go
    @@ -1112,11 +1112,11 @@ struct __go_string GoString(char *p) {\n     	return __go_byte_array_to_string(p, len);\n     }\n     \n    -struct __go_string GoStringN(char *p, intgo n) {\n    +struct __go_string GoStringN(char *p, int32_t n) {\n     	return __go_byte_array_to_string(p, n);\n     }\n     \n    -Slice GoBytes(char *p, intgo n) {\n    +Slice GoBytes(char *p, int32_t n) {\n     	struct __go_string s = { (const unsigned char *)p, n };\n     	return __go_string_to_byte_array(s);\n     }\
    

コアとなるコードの解説

このコミットは、cgoが生成するCコードとGoランタイムの間の型安全性を向上させることを目的としています。

  • _GoString_ GoStringN(char *p, int l); および _GoBytes_ GoBytes(void *p, int n);: これらは、cgoが生成するCコード内でGoの文字列やバイトスライスを扱うための内部的なC関数宣言です。以前は長さ引数にintgoを使用していましたが、これをCの標準的なintに変更することで、Cコンパイラが期待する型との整合性を高めています。intは通常32ビット幅であり、Goのint32と互換性があります。

  • void ·_Cfunc_GoStringN(int8 *p, int32 l, String s) および void ·_Cfunc_GoBytes(int8 *p, int32 l, Slice s): これらは、GoのコードからCの関数を呼び出す際にcgoが生成するラッパー関数です。Goのstring[]byteをCのポインタと長さに変換し、Goランタイムの内部関数(runtime·gostringnruntime·gobytes)を呼び出します。ここで引数型をint32に明示的に指定することで、Goの固定幅型との整合性を保証し、異なるアーキテクチャ間での型不一致によるビルドエラーを防ぎます。

  • struct __go_string GoStringN(char *p, int32_t n) および Slice GoBytes(char *p, int32_t n): これらは、CのコードからGoの文字列やバイトスライスを生成するための内部的なC関数実装です。Cのポインタと長さをGoの内部表現に変換します。ここで長さ引数にCの固定幅型であるint32_tを使用することで、Goのint32型とCの型が確実に一致するようにしています。これにより、GoとCの間でデータが正しくやり取りされることが保証されます。

これらの変更は、cgoが生成するコードの堅牢性とポータビリティを向上させ、Goのint型がプラットフォームによって異なるサイズを持つことによる潜在的な問題を回避するために重要です。

関連リンク

参考にした情報源リンク