Skip to content

Commit 6fa0ace

Browse files
[release-branch.go1.11] cmd/cgo: use field alignment when setting field offset
The old code ignored the field alignment, and only looked at the field offset: if the field offset required padding, cgo added padding. But while that approach works for Go (at least with the gc toolchain) it doesn't work for C code using packed structs. With a packed struct the added padding may leave the struct at a misaligned position, and the inserted alignment, which cgo is not considering, may introduce additional, unexpected, padding. Padding that ignores alignment is not a good idea when the struct is not packed, and Go structs are never packed. So don't ignore alignment. Updates #28896 Fixes #28916 Change-Id: Ie50ea15fa6dc35557497097be9fecfecb11efd8a Reviewed-on: https://go-review.googlesource.com/c/150602 Run-TryBot: Ian Lance Taylor <[email protected]> Reviewed-by: Bryan C. Mills <[email protected]> (cherry picked from commit fbdaa96) Reviewed-on: https://go-review.googlesource.com/c/151778 TryBot-Result: Gobot Gobot <[email protected]>
1 parent 6971090 commit 6fa0ace

File tree

3 files changed

+97
-5
lines changed

3 files changed

+97
-5
lines changed

misc/cgo/test/cgo_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ func Test23356(t *testing.T) { test23356(t) }
9393
func Test26066(t *testing.T) { test26066(t) }
9494
func Test26213(t *testing.T) { test26213(t) }
9595
func Test27660(t *testing.T) { test27660(t) }
96+
func Test28896(t *testing.T) { test28896(t) }
9697

9798
func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) }
9899
func BenchmarkGoString(b *testing.B) { benchGoString(b) }

misc/cgo/test/issue28896.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Copyright 2018 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// cgo was incorrectly adding padding after a packed struct.
6+
7+
package cgotest
8+
9+
/*
10+
#include <stddef.h>
11+
#include <stdint.h>
12+
#include <stdlib.h>
13+
14+
typedef struct {
15+
void *f1;
16+
uint32_t f2;
17+
} __attribute__((__packed__)) innerPacked;
18+
19+
typedef struct {
20+
innerPacked g1;
21+
uint64_t g2;
22+
} outerPacked;
23+
24+
typedef struct {
25+
void *f1;
26+
uint32_t f2;
27+
} innerUnpacked;
28+
29+
typedef struct {
30+
innerUnpacked g1;
31+
uint64_t g2;
32+
} outerUnpacked;
33+
34+
size_t offset(int x) {
35+
switch (x) {
36+
case 0:
37+
return offsetof(innerPacked, f2);
38+
case 1:
39+
return offsetof(outerPacked, g2);
40+
case 2:
41+
return offsetof(innerUnpacked, f2);
42+
case 3:
43+
return offsetof(outerUnpacked, g2);
44+
default:
45+
abort();
46+
}
47+
}
48+
*/
49+
import "C"
50+
51+
import (
52+
"testing"
53+
"unsafe"
54+
)
55+
56+
func offset(i int) uintptr {
57+
var pi C.innerPacked
58+
var po C.outerPacked
59+
var ui C.innerUnpacked
60+
var uo C.outerUnpacked
61+
switch i {
62+
case 0:
63+
return unsafe.Offsetof(pi.f2)
64+
case 1:
65+
return unsafe.Offsetof(po.g2)
66+
case 2:
67+
return unsafe.Offsetof(ui.f2)
68+
case 3:
69+
return unsafe.Offsetof(uo.g2)
70+
default:
71+
panic("can't happen")
72+
}
73+
}
74+
75+
func test28896(t *testing.T) {
76+
for i := 0; i < 4; i++ {
77+
c := uintptr(C.offset(C.int(i)))
78+
g := offset(i)
79+
if c != g {
80+
t.Errorf("%d: C: %d != Go %d", i, c, g)
81+
}
82+
}
83+
}

src/cmd/cgo/gcc.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2462,11 +2462,6 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct
24622462

24632463
anon := 0
24642464
for _, f := range dt.Field {
2465-
if f.ByteOffset > off {
2466-
fld, sizes = c.pad(fld, sizes, f.ByteOffset-off)
2467-
off = f.ByteOffset
2468-
}
2469-
24702465
name := f.Name
24712466
ft := f.Type
24722467

@@ -2515,6 +2510,19 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct
25152510
// structs are in system headers that cannot be corrected.
25162511
continue
25172512
}
2513+
2514+
// Round off up to talign, assumed to be a power of 2.
2515+
off = (off + talign - 1) &^ (talign - 1)
2516+
2517+
if f.ByteOffset > off {
2518+
fld, sizes = c.pad(fld, sizes, f.ByteOffset-off)
2519+
off = f.ByteOffset
2520+
}
2521+
if f.ByteOffset < off {
2522+
// Drop a packed field that we can't represent.
2523+
continue
2524+
}
2525+
25182526
n := len(fld)
25192527
fld = fld[0 : n+1]
25202528
if name == "" {

0 commit comments

Comments
 (0)