[インデックス 19343] ファイルの概要
このコミットは、Goのcgo
ツールにおいて、C言語の構造体フィールドがGoのメモリレイアウトと互換性のないアライメントを持つ場合に、そのフィールドをGoの構造体から除外する(スキップする)変更を導入します。これは、ビットフィールドがGoの構造体から除外されるのと同様の扱いです。この変更により、C言語のヘッダファイルに含まれる、Goから直接アクセスできないような特殊な構造体定義(例えば、アライメントが厳密に制御されたり、パディングが意図的に挿入されたりするケース)が原因で発生するコンパイルエラーやランタイムエラーを防ぎ、cgo
の堅牢性を向上させます。
コミット
commit 2d1a9510edc59cad463029fdc8ac93e62247baad
Author: Russ Cox <rsc@golang.org>
Date: Mon May 12 23:48:20 2014 -0400
cmd/cgo: omit misaligned struct fields, like we omit bitfields
Fixes #7560.
LGTM=iant
R=golang-codereviews, iant
CC=golang-codereviews
https://golang.org/cl/96300045
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/2d1a9510edc59cad463029fdc8ac93e62247baad
元コミット内容
このコミットは、cmd/cgo
(GoのC言語バインディングツール)において、アライメントが不正な構造体フィールドを、ビットフィールドと同様にGoの構造体から省略する(除外する)ことを目的としています。これにより、GoとCの間の構造体レイアウトの不一致によって引き起こされる問題を解決し、Issue #7560を修正します。
変更の背景
GoプログラムがC言語のライブラリと連携するためにcgo
を使用する場合、C言語で定義された構造体はGoの型にマッピングされます。このマッピングにおいて、C言語の構造体のメモリレイアウトとGoのメモリレイアウトの間に不一致が生じることがあります。特に、フィールドのアライメント(メモリ上での配置規則)が異なる場合に問題が発生します。
Goは効率的なメモリアクセスとパフォーマンスのために、特定のデータ型に対して厳密なメモリアライメント要件を持っています。一方、C言語の構造体は、コンパイラやプラットフォーム、あるいは__attribute__((__packed__))
のような特定の属性によって、Goとは異なるアライメントを持つことがあります。
このようなアライメントの不一致は、以下のような深刻な問題を引き起こす可能性があります。
- データ破損: メモリの誤った位置にアクセスすることで、データの読み書きが不正になり、デバッグが困難なバグにつながります。
- クラッシュ: 一部のアーキテクチャやオペレーティングシステムでは、アライメントされていないデータへのアクセスがハードウェア例外を引き起こし、プログラムがクラッシュする原因となります。
- パフォーマンス低下: アライメントされていないアクセスは、CPUが単一のフィールドにアクセスするために複数のメモリ操作を実行する必要があるため、著しく遅くなる可能性があります。
- 偽共有 (False Sharing): 並行プログラムにおいて、互換性のないアライメントのために異なるゴルーチンのフィールドが同じキャッシュラインに配置されると、「偽共有」が発生し、無関係なデータアクセスがキャッシュラインを無効化し、パフォーマンスを低下させます。
このコミットは、GitHub Issue #7560「cmd/cgo: reject C structs with Go-incompatible field alignment」に関連しています。元々は、Goと互換性のないフィールドアライメントを持つC構造体をcgo
が拒否するという提案でしたが、このコミットでは、より柔軟なアプローチとして、互換性のないフィールドをGoの構造体から「省略する」ことで、コンパイルエラーを回避しつつ、Goからアクセス可能な部分のみを公開するように変更されました。これにより、特にシステムヘッダなど、修正が困難なC言語の構造体定義を含むプロジェクトでも、cgo
をより使いやすくすることが目的です。
前提知識の解説
Cgo
Cgoは、GoプログラムからC言語のコードを呼び出すためのGoのツールです。Goのソースファイル内にC言語のコードを直接記述し、Goの関数からCの関数を呼び出したり、Cの構造体をGoの型として扱ったりすることができます。cgo
は、GoとCの間のデータ型の変換や、関数呼び出しの橋渡しを行います。
構造体のアライメント (Struct Alignment)
構造体のアライメントとは、構造体の各メンバーがメモリ上で配置される際の、特定のバイト境界への制約のことです。CPUは通常、特定のデータ型を特定のバイト境界(例えば、4バイト整数は4バイト境界、8バイト整数は8バイト境界)に配置されている場合に最も効率的にアクセスできます。アライメント要件を満たすために、コンパイラは構造体のメンバー間にパディング(埋め草)を挿入することがあります。
例:
struct MyStruct {
char a; // 1バイト
int b; // 4バイト (4バイト境界に配置されるため、aの後に3バイトのパディングが入る可能性がある)
char c; // 1バイト
};
この構造体は、char
とint
の間にパディングが挿入されることで、int b
が4バイト境界に配置されるように調整されることがあります。
ビットフィールド (Bitfields)
ビットフィールドは、C言語の構造体において、メンバーがビット単位でメモリを占有するように指定する機能です。例えば、1ビットのフラグや3ビットの数値などを定義できます。
例:
struct Flags {
unsigned int flag1 : 1; // 1ビット
unsigned int flag2 : 1; // 1ビット
unsigned int value : 3; // 3ビット
};
Goにはビットフィールドに直接対応する機能がないため、cgo
は通常、ビットフィールドを含むC構造体をGoの型にマッピングする際に、ビットフィールドをGoの構造体から除外します。これは、Goがビット単位のメモリ操作を直接サポートしないためです。
__attribute__((__packed__))
GCCなどのコンパイラ拡張機能で、構造体や共用体のメンバー間のパディングを最小限に抑える、または完全に削除するように指示する属性です。これにより、構造体のサイズを小さくできますが、アライメント要件が満たされないためにパフォーマンスが低下したり、特定のアーキテクチャでクラッシュしたりするリスクがあります。
例:
typedef struct {
char x;
long y;
} __attribute__((__packed__)) misaligned;
この例では、misaligned
構造体はpacked
属性を持つため、char x
の直後にlong y
が配置され、通常挿入されるパディングが省略されます。これにより、y
がその自然なアライメント境界(例えば8バイト境界)に配置されない可能性があります。
uintptr_t
C言語の標準ライブラリで定義されている型で、ポインタを保持できる十分な大きさの符号なし整数型です。主にポインタ演算や、ポインタを整数として扱う必要がある場合に使用されます。このコミットのテストコードでは、構造体メンバーのオフセットを計算するために使用されています。
Goのreflect
パッケージ
Goのreflect
パッケージは、実行時にGoのプログラムの構造(型、フィールド、メソッドなど)を検査・操作するための機能を提供します。このコミットのテストコードでは、C.misaligned
構造体がGoにどのようにマッピングされたか(フィールドの数や名前)を検証するために使用されています。
技術的詳細
このコミットの主要な変更は、src/cmd/cgo/gcc.go
内のtypeConv.Struct
関数にあります。この関数は、C言語の構造体型をGoの抽象構文木(AST)における構造体型に変換する役割を担っています。
変更前は、cgo
はビットフィールドをGoの構造体から除外していましたが、アライメントが不正な通常のフィールドについては、Goの構造体へのマッピングを試みていました。しかし、これがGoのメモリモデルと衝突し、前述のような問題を引き起こす可能性がありました。
このコミットでは、ビットフィールドと同様に、アライメントが不正なフィールドもGoの構造体から除外するロジックが追加されました。具体的には、以下のチェックが導入されました。
- フィールドのバイトオフセット(構造体の先頭からの距離)が、そのフィールドの型のアライメント要件で割り切れない場合(つまり、アライメントが不正な場合)。
- この条件が満たされる場合、そのフィールドはGoの構造体からスキップされます(
continue
文によって、そのフィールドの処理が中断され、次のフィールドに移ります)。
この変更の意図は、Goからアクセス可能なフィールドのみをGoの構造体に含めることで、cgo
によって生成されるGoコードの堅牢性を高めることです。これにより、C言語のヘッダファイルがGoの厳密なアライメント要件を満たさない場合でも、cgo
がエラーを発生させることなく、Goプログラムがコンパイルされ、実行できるようになります。特に、ユーザーが制御できないシステムヘッダなど、修正が困難なCの構造体定義を扱う際に有効です。
テストケースmisc/cgo/test/issue7560.go
では、__attribute__((__packed__))
を使用して意図的にアライメントが不正になるCの構造体misaligned
を定義し、そのy
フィールドがGoの構造体から正しく除外されることを検証しています。Goのreflect
パッケージを使って、GoにマッピングされたC.misaligned
型が、x
フィールドとパディングのための匿名フィールド(_
)のみを持ち、y
フィールドを持たないことを確認しています。
また、src/cmd/cgo/doc.go
には、この新しい挙動に関するドキュメントが追加され、C構造体のビットフィールドやアライメントが不正なデータがGoの構造体から省略され、適切なパディングに置き換えられることが明記されました。
コアとなるコードの変更箇所
src/cmd/cgo/gcc.go
--- a/src/cmd/cgo/gcc.go
+++ b/src/cmd/cgo/gcc.go
@@ -1499,7 +1499,7 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct
t := c.Type(f.Type, pos)
tgo := t.Go
size := t.Size
-
+ talign := t.Align
if f.BitSize > 0 {
if f.BitSize%8 != 0 {
continue
@@ -1512,8 +1512,17 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct
name = "uint"
}
tgo = ast.NewIdent(name + fmt.Sprint(f.BitSize))
+ talign = size
}
+ if talign > 0 && f.ByteOffset%talign != 0 {
+ // Drop misaligned fields, the same way we drop integer bit fields.
+ // The goal is to make available what can be made available.
+ // Otherwise one bad and unneeded field in an otherwise okay struct
+ // makes the whole program not compile. Much of the time these
+ // structs are in system headers that cannot be corrected.
+ continue
+ }
n := len(fld)
fld = fld[0 : n+1]
name := f.Name
@@ -1528,8 +1537,8 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct
buf.WriteString(" ")
buf.WriteString(name)
buf.WriteString("; ")
- if t.Align > align {
- align = t.Align
+ if talign > align {
+ align = talign
}
}
if off < dt.ByteSize {
src/cmd/cgo/doc.go
--- a/src/cmd/cgo/doc.go
+++ b/src/cmd/cgo/doc.go
@@ -68,6 +68,9 @@ Go references to C
Within the Go file, C's struct field names that are keywords in Go
can be accessed by prefixing them with an underscore: if x points at a C
struct with a field named "type", x._type accesses the field.
+C struct fields that cannot be expressed in Go, such as bit fields
+or misaligned data, are omitted in the Go struct, replaced by
+appropriate padding to reach the next field or the end of the struct.
The standard C numeric types are available under the names
C.char, C.schar (signed char), C.uchar (unsigned char),
misc/cgo/test/issue7560.go
(新規ファイル)
このファイルは、__attribute__((__packed__))
を使用して意図的にアライメントが不正な構造体を定義し、そのフィールドがGoの構造体から正しく除外されることを検証するテストケースです。
コアとなるコードの解説
src/cmd/cgo/gcc.go
の変更点
-
talign := t.Align
の追加:- 元のコードでは、フィールドの型のアライメント情報が
t.Align
として取得されていましたが、ビットフィールドの場合にtalign
という新しい変数にsize
を代入することで、ビットフィールドのアライメントを適切に扱うための準備をしています。
- 元のコードでは、フィールドの型のアライメント情報が
-
アライメントチェックとフィールドの省略ロジックの追加:
if talign > 0 && f.ByteOffset%talign != 0 { // Drop misaligned fields, the same way we drop integer bit fields. // The goal is to make available what can be made available. // Otherwise one bad and unneeded field in an otherwise okay struct // makes the whole program not compile. Much of the time these // structs are in system headers that cannot be corrected. continue }
talign > 0
: アライメント情報が有効であることを確認します。f.ByteOffset%talign != 0
: フィールドのバイトオフセット(f.ByteOffset
)が、そのフィールドの型のアライメント要件(talign
)で割り切れない場合、つまりアライメントが不正な場合に真となります。- この条件が真の場合、
continue
文が実行され、現在のフィールドの処理をスキップして、次のフィールドの処理に移ります。 - コメントには、この変更の意図が明確に記述されています。「ビットフィールドを省略するのと同じように、アライメントが不正なフィールドも省略する。目標は、利用可能なものを利用可能にすることである。そうでなければ、問題のない構造体の中に一つでも不正で不要なフィールドがあると、プログラム全体がコンパイルできなくなる。多くの場合、これらの構造体は修正できないシステムヘッダにある。」
-
align = talign
への変更:- 構造体全体の最大アライメントを計算する際に、以前は
t.Align
を使用していましたが、ビットフィールドの場合に設定されたtalign
を使用するように変更されています。これにより、ビットフィールドを含む構造体のアライメント計算がより正確になります。
- 構造体全体の最大アライメントを計算する際に、以前は
これらの変更により、cgo
はC言語の構造体をGoの型に変換する際に、Goのメモリモデルと互換性のないフィールドを自動的に検出し、それらをGoの構造体から除外するようになりました。これにより、GoとCの間の相互運用性が向上し、特に既存のCライブラリをGoから利用する際の堅牢性が高まります。
src/cmd/cgo/doc.go
の変更点
- Cgoのドキュメントに、C構造体のビットフィールドやアライメントが不正なデータがGoの構造体から省略され、適切なパディングに置き換えられるという説明が追加されました。これにより、
cgo
の挙動がより明確になり、開発者が予期せぬ挙動に遭遇するのを防ぎます。
misc/cgo/test/issue7560.go
の新規追加
- このテストファイルは、
__attribute__((__packed__))
を使用して意図的にアライメントが不正なC構造体misaligned
を定義しています。 C.offset7560()
関数は、misaligned
構造体内のy
フィールドのオフセットを返します。これにより、Cコンパイラが実際に構造体をパックしたかどうかを確認します。- Goの
test7560
関数では、C.misaligned
型をGoのreflect
パッケージで検査し、y
フィールドがGoの構造体から除外され、代わりにパディングを表す匿名フィールド(_
)が存在することを確認します。これにより、cgo
が期待通りにアライメントが不正なフィールドを省略していることを検証します。
関連リンク
- Go CL: https://golang.org/cl/96300045
- GitHub Issue: https://github.com/golang/go/issues/7560
参考にした情報源リンク
- Web search results for "golang.org/cl/96300045" (provided by the tool)
- Go言語の公式ドキュメント (Cgoに関する一般的な知識)
- C言語の構造体アライメントとビットフィールドに関する一般的な知識I have generated the detailed technical explanation in Markdown format, following all the user's instructions, including the chapter structure and language. I have used the extracted commit information and the context gathered from the web search. The output is now ready to be printed to standard output.
# [インデックス 19343] ファイルの概要
このコミットは、Goの`cgo`ツールにおいて、C言語の構造体フィールドがGoのメモリレイアウトと互換性のないアライメントを持つ場合に、そのフィールドをGoの構造体から除外する(スキップする)変更を導入します。これは、ビットフィールドがGoの構造体から除外されるのと同様の扱いです。この変更により、C言語のヘッダファイルに含まれる、Goから直接アクセスできないような特殊な構造体定義(例えば、アライメントが厳密に制御されたり、パディングが意図的に挿入されたりするケース)が原因で発生するコンパイルエラーやランタイムエラーを防ぎ、`cgo`の堅牢性を向上させます。
## コミット
commit 2d1a9510edc59cad463029fdc8ac93e62247baad Author: Russ Cox rsc@golang.org Date: Mon May 12 23:48:20 2014 -0400
cmd/cgo: omit misaligned struct fields, like we omit bitfields
Fixes #7560.
LGTM=iant
R=golang-codereviews, iant
CC=golang-codereviews
https://golang.org/cl/96300045
## GitHub上でのコミットページへのリンク
[https://github.com/golang/go/commit/2d1a9510edc59cad463029fdc8ac93e62247baad](https://github.com/golang/go/commit/2d1a9510edc59cad463029fdc8ac93e62247baad)
## 元コミット内容
このコミットは、`cmd/cgo`(GoのC言語バインディングツール)において、アライメントが不正な構造体フィールドを、ビットフィールドと同様にGoの構造体から省略する(除外する)ことを目的としています。これにより、GoとCの間の構造体レイアウトの不一致によって引き起こされる問題を解決し、Issue #7560を修正します。
## 変更の背景
GoプログラムがC言語のライブラリと連携するために`cgo`を使用する場合、C言語で定義された構造体はGoの型にマッピングされます。このマッピングにおいて、C言語の構造体のメモリレイアウトとGoのメモリレイアウトの間に不一致が生じることがあります。特に、フィールドのアライメント(メモリ上での配置規則)が異なる場合に問題が発生します。
Goは効率的なメモリアクセスとパフォーマンスのために、特定のデータ型に対して厳密なメモリアライメント要件を持っています。一方、C言語の構造体は、コンパイラやプラットフォーム、あるいは`__attribute__((__packed__))`のような特定の属性によって、Goとは異なるアライメントを持つことがあります。
このようなアライメントの不一致は、以下のような深刻な問題を引き起こす可能性があります。
* **データ破損**: メモリの誤った位置にアクセスすることで、データの読み書きが不正になり、デバッグが困難なバグにつながります。
* **クラッシュ**: 一部のアーキテクチャやオペレーティングシステムでは、アライメントされていないデータへのアクセスがハードウェア例外を引き起こし、プログラムがクラッシュする原因となります。
* **パフォーマンス低下**: アライメントされていないアクセスは、CPUが単一のフィールドにアクセスするために複数のメモリ操作を実行する必要があるため、著しく遅くなる可能性があります。
* **偽共有 (False Sharing)**: 並行プログラムにおいて、互換性のないアライメントのために異なるゴルーチンのフィールドが同じキャッシュラインに配置されると、「偽共有」が発生し、無関係なデータアクセスがキャッシュラインを無効化し、パフォーマンスを低下させます。
このコミットは、GitHub Issue #7560「cmd/cgo: reject C structs with Go-incompatible field alignment」に関連しています。元々は、Goと互換性のないフィールドアライメントを持つC構造体を`cgo`が拒否するという提案でしたが、このコミットでは、より柔軟なアプローチとして、互換性のないフィールドをGoの構造体から「省略する」ことで、コンパイルエラーを回避しつつ、Goからアクセス可能な部分のみを公開するように変更されました。これにより、特にシステムヘッダなど、修正が困難なC言語の構造体定義を含むプロジェクトでも、`cgo`をより使いやすくすることが目的です。
## 前提知識の解説
### Cgo
Cgoは、GoプログラムからC言語のコードを呼び出すためのGoのツールです。Goのソースファイル内にC言語のコードを直接記述し、Goの関数からCの関数を呼び出したり、Cの構造体をGoの型として扱ったりすることができます。`cgo`は、GoとCの間のデータ型の変換や、関数呼び出しの橋渡しを行います。
### 構造体のアライメント (Struct Alignment)
構造体のアライメントとは、構造体の各メンバーがメモリ上で配置される際の、特定のバイト境界への制約のことです。CPUは通常、特定のデータ型を特定のバイト境界(例えば、4バイト整数は4バイト境界、8バイト整数は8バイト境界)に配置されている場合に最も効率的にアクセスできます。アライメント要件を満たすために、コンパイラは構造体のメンバー間にパディング(埋め草)を挿入することがあります。
例:
```c
struct MyStruct {
char a; // 1バイト
int b; // 4バイト (4バイト境界に配置されるため、aの後に3バイトのパディングが入る可能性がある)
char c; // 1バイト
};
この構造体は、char
とint
の間にパディングが挿入されることで、int b
が4バイト境界に配置されるように調整されることがあります。
ビットフィールド (Bitfields)
ビットフィールドは、C言語の構造体において、メンバーがビット単位でメモリを占有するように指定する機能です。例えば、1ビットのフラグや3ビットの数値などを定義できます。
例:
struct Flags {
unsigned int flag1 : 1; // 1ビット
unsigned int flag2 : 1; // 1ビット
unsigned int value : 3; // 3ビット
};
Goにはビットフィールドに直接対応する機能がないため、cgo
は通常、ビットフィールドを含むC構造体をGoの型にマッピングする際に、ビットフィールドをGoの構造体から除外します。これは、Goがビット単位のメモリ操作を直接サポートしないためです。
__attribute__((__packed__))
GCCなどのコンパイラ拡張機能で、構造体や共用体のメンバー間のパディングを最小限に抑える、または完全に削除するように指示する属性です。これにより、構造体のサイズを小さくできますが、アライメント要件が満たされないためにパフォーマンスが低下したり、特定のアーキテクチャでクラッシュしたりするリスクがあります。
例:
typedef struct {
char x;
long y;
} __attribute__((__packed__)) misaligned;
この例では、misaligned
構造体はpacked
属性を持つため、char x
の直後にlong y
が配置され、通常挿入されるパディングが省略されます。これにより、y
がその自然なアライメント境界(例えば8バイト境界)に配置されない可能性があります。
uintptr_t
C言語の標準ライブラリで定義されている型で、ポインタを保持できる十分な大きさの符号なし整数型です。主にポインタ演算や、ポインタを整数として扱う必要がある場合に使用されます。このコミットのテストコードでは、構造体メンバーのオフセットを計算するために使用されています。
Goのreflect
パッケージ
Goのreflect
パッケージは、実行時にGoのプログラムの構造(型、フィールド、メソッドなど)を検査・操作するための機能を提供します。このコミットのテストコードでは、C.misaligned
構造体がGoにどのようにマッピングされたか(フィールドの数や名前)を検証するために使用されています。
技術的詳細
このコミットの主要な変更は、src/cmd/cgo/gcc.go
内のtypeConv.Struct
関数にあります。この関数は、C言語の構造体型をGoの抽象構文木(AST)における構造体型に変換する役割を担っています。
変更前は、cgo
はビットフィールドをGoの構造体から除外していましたが、アライメントが不正な通常のフィールドについては、Goの構造体へのマッピングを試みていました。しかし、これがGoのメモリモデルと衝突し、前述のような問題を引き起こす可能性がありました。
このコミットでは、ビットフィールドと同様に、アライメントが不正なフィールドもGoの構造体から除外するロジックが追加されました。具体的には、以下のチェックが導入されました。
- フィールドのバイトオフセット(構造体の先頭からの距離)が、そのフィールドの型のアライメント要件で割り切れない場合(つまり、アライメントが不正な場合)。
- この条件が満たされる場合、そのフィールドはGoの構造体からスキップされます(
continue
文によって、そのフィールドの処理が中断され、次のフィールドに移ります)。
この変更の意図は、Goからアクセス可能なフィールドのみをGoの構造体に含めることで、cgo
によって生成されるGoコードの堅牢性を高めることです。これにより、C言語のヘッダファイルがGoの厳密なアライメント要件を満たさない場合でも、cgo
がエラーを発生させることなく、Goプログラムがコンパイルされ、実行できるようになります。特に、ユーザーが制御できないシステムヘッダなど、修正が困難なCの構造体定義を扱う際に有効です。
テストケースmisc/cgo/test/issue7560.go
では、__attribute__((__packed__))
を使用して意図的にアライメントが不正になるCの構造体misaligned
を定義し、そのy
フィールドがGoの構造体から正しく除外されることを検証しています。Goのreflect
パッケージを使って、GoにマッピングされたC.misaligned
型が、x
フィールドとパディングのための匿名フィールド(_
)のみを持ち、y
フィールドを持たないことを確認しています。
また、src/cmd/cgo/doc.go
には、この新しい挙動に関するドキュメントが追加され、C構造体のビットフィールドやアライメントが不正なデータがGoの構造体から省略され、適切なパディングに置き換えられることが明記されました。
コアとなるコードの変更箇所
src/cmd/cgo/gcc.go
--- a/src/cmd/cgo/gcc.go
+++ b/src/cmd/cgo/gcc.go
@@ -1499,7 +1499,7 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct
t := c.Type(f.Type, pos)
tgo := t.Go
size := t.Size
-
+ talign := t.Align
if f.BitSize > 0 {
if f.BitSize%8 != 0 {
continue
@@ -1512,8 +1512,17 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct
name = "uint"
}
tgo = ast.NewIdent(name + fmt.Sprint(f.BitSize))
+ talign = size
}
+ if talign > 0 && f.ByteOffset%talign != 0 {
+ // Drop misaligned fields, the same way we drop integer bit fields.
+ // The goal is to make available what can be made available.
+ // Otherwise one bad and unneeded field in an otherwise okay struct
+ // makes the whole program not compile. Much of the time these
+ // structs are in system headers that cannot be corrected.
+ continue
+ }
n := len(fld)
fld = fld[0 : n+1]
name := f.Name
@@ -1528,8 +1537,8 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct
buf.WriteString(" ")
buf.WriteString(name)
buf.WriteString("; ")
- if t.Align > align {
- align = t.Align
+ if talign > align {
+ align = talign
}
}
if off < dt.ByteSize {
src/cmd/cgo/doc.go
--- a/src/cmd/cgo/doc.go
+++ b/src/cmd/cgo/doc.go
@@ -68,6 +68,9 @@ Go references to C
Within the Go file, C's struct field names that are keywords in Go
can be accessed by prefixing them with an underscore: if x points at a C
struct with a field named "type", x._type accesses the field.
+C struct fields that cannot be expressed in Go, such as bit fields
+or misaligned data, are omitted in the Go struct, replaced by
+appropriate padding to reach the next field or the end of the struct.
The standard C numeric types are available under the names
C.char, C.schar (signed char), C.uchar (unsigned char),
misc/cgo/test/issue7560.go
(新規ファイル)
このファイルは、__attribute__((__packed__))
を使用して意図的にアライメントが不正な構造体を定義し、そのフィールドがGoの構造体から正しく除外されることを検証するテストケースです。
コアとなるコードの解説
src/cmd/cgo/gcc.go
の変更点
-
talign := t.Align
の追加:- 元のコードでは、フィールドの型のアライメント情報が
t.Align
として取得されていましたが、ビットフィールドの場合にtalign
という新しい変数にsize
を代入することで、ビットフィールドのアライメントを適切に扱うための準備をしています。
- 元のコードでは、フィールドの型のアライメント情報が
-
アライメントチェックとフィールドの省略ロジックの追加:
if talign > 0 && f.ByteOffset%talign != 0 { // Drop misaligned fields, the same way we drop integer bit fields. // The goal is to make available what can be made available. // Otherwise one bad and unneeded field in an otherwise okay struct // makes the whole program not compile. Much of the time these // structs are in system headers that cannot be corrected. continue }
talign > 0
: アライメント情報が有効であることを確認します。f.ByteOffset%talign != 0
: フィールドのバイトオフセット(f.ByteOffset
)が、そのフィールドの型のアライメント要件(talign
)で割り切れない場合、つまりアライメントが不正な場合に真となります。- この条件が真の場合、
continue
文が実行され、現在のフィールドの処理をスキップして、次のフィールドの処理に移ります。 - コメントには、この変更の意図が明確に記述されています。「ビットフィールドを省略するのと同じように、アライメントが不正なフィールドも省略する。目標は、利用可能なものを利用可能にすることである。そうでなければ、問題のない構造体の中に一つでも不正で不要なフィールドがあると、プログラム全体がコンパイルできなくなる。多くの場合、これらの構造体は修正できないシステムヘッダにある。」
-
align = talign
への変更:- 構造体全体の最大アライメントを計算する際に、以前は
t.Align
を使用していましたが、ビットフィールドの場合に設定されたtalign
を使用するように変更されています。これにより、ビットフィールドを含む構造体のアライメント計算がより正確になります。
- 構造体全体の最大アライメントを計算する際に、以前は
これらの変更により、cgo
はC言語の構造体をGoの型に変換する際に、Goのメモリモデルと互換性のないフィールドを自動的に検出し、それらをGoの構造体から除外するようになりました。これにより、GoとCの間の相互運用性が向上し、特に既存のCライブラリをGoから利用する際の堅牢性が高まります。
src/cmd/cgo/doc.go
の変更点
- Cgoのドキュメントに、C構造体のビットフィールドやアライメントが不正なデータがGoの構造体から省略され、適切なパディングに置き換えられるという説明が追加されました。これにより、
cgo
の挙動がより明確になり、開発者が予期せぬ挙動に遭遇するのを防ぎます。
misc/cgo/test/issue7560.go
の新規追加
- このテストファイルは、
__attribute__((__packed__))
を使用して意図的にアライメントが不正なC構造体misaligned
を定義しています。 C.offset7560()
関数は、misaligned
構造体内のy
フィールドのオフセットを返します。これにより、Cコンパイラが実際に構造体をパックしたかどうかを確認します。- Goの
test7560
関数では、C.misaligned
型をGoのreflect
パッケージで検査し、y
フィールドがGoの構造体から除外され、代わりにパディングを表す匿名フィールド(_
)が存在することを確認します。これにより、cgo
が期待通りにアライメントが不正なフィールドを省略していることを検証します。
関連リンク
- Go CL: https://golang.org/cl/96300045
- GitHub Issue: https://github.com/golang/go/issues/7560
参考にした情報源リンク
- Web search results for "golang.org/cl/96300045" (provided by the tool)
- Go言語の公式ドキュメント (Cgoに関する一般的な知識)
- C言語の構造体アライメントとビットフィールドに関する一般的な知識