Skip to content

Commit 1a0c8fe

Browse files
committed
cmd/cgo: bug fixes
* disallow embedding of C type (Fixes issue 2552) * detect 0-length array (Fixes issue 2806) * use typedefs when possible, to avoid attribute((unavailable)) (Fixes issue 2888) * print Go types constructed from C types using original C types (Fixes issue 2612) This fix changes _cgo_export.h to repeat the preamble from import "C". Otherwise the fix to issue 2612 is impossible, since it cannot refer to types that have not been defined. If people are using //export and putting non-header information in the preamble, they will need to refactor their code. R=golang-dev, r, r CC=golang-dev https://golang.org/cl/5672080
1 parent 72fb81e commit 1a0c8fe

File tree

14 files changed

+157
-35
lines changed

14 files changed

+157
-35
lines changed

doc/go1.html

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1853,7 +1853,24 @@ <h3 id="url">The url package</h3>
18531853
The semantic changes make it difficult for the fix tool to update automatically.
18541854
</p>
18551855

1856-
<h2 id="go_command">The go command</h2>
1856+
<h2 id="cmd_go">The go command</h2>
1857+
1858+
<p>
1859+
TODO: Write this.
1860+
</p>
1861+
1862+
<h2 id="cmd_cgo">The cgo command</h2>
1863+
1864+
<p>
1865+
In Go 1, the <a href="/cmd/cgo">cgo command</a>
1866+
uses a different <code>_cgo_export.h</code>
1867+
file, which is generated for packages containing <code>//export</code> lines.
1868+
The <code>_cgo_export.h</code> file now begins with the C preamble comment,
1869+
so that exported function definitions can use types defined there.
1870+
This has the effect of compiling the preamble multiple times, so a
1871+
package using <code>//export</code> must not put function definitions
1872+
or variable initializations in the C preamble.
1873+
</p
18571874

18581875
<h2 id="releases">Packaged releases</h2>
18591876

doc/go1.tmpl

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1725,7 +1725,24 @@ Code that uses the old fields will fail to compile and must be updated by hand.
17251725
The semantic changes make it difficult for the fix tool to update automatically.
17261726
</p>
17271727

1728-
<h2 id="go_command">The go command</h2>
1728+
<h2 id="cmd_go">The go command</h2>
1729+
1730+
<p>
1731+
TODO: Write this.
1732+
</p>
1733+
1734+
<h2 id="cmd_cgo">The cgo command</h2>
1735+
1736+
<p>
1737+
In Go 1, the <a href="/cmd/cgo">cgo command</a>
1738+
uses a different <code>_cgo_export.h</code>
1739+
file, which is generated for packages containing <code>//export</code> lines.
1740+
The <code>_cgo_export.h</code> file now begins with the C preamble comment,
1741+
so that exported function definitions can use types defined there.
1742+
This has the effect of compiling the preamble multiple times, so a
1743+
package using <code>//export</code> must not put function definitions
1744+
or variable initializations in the C preamble.
1745+
</p
17291746

17301747
<h2 id="releases">Packaged releases</h2>
17311748

src/cmd/cgo/ast.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,9 @@ func (f *File) saveRef(x interface{}, context string) {
147147
if context == "as2" {
148148
context = "expr"
149149
}
150+
if context == "embed-type" {
151+
error_(sel.Pos(), "cannot embed C type")
152+
}
150153
goname := sel.Sel.Name
151154
if goname == "errno" {
152155
error_(sel.Pos(), "cannot refer to errno directly; see documentation")
@@ -232,7 +235,11 @@ func (f *File) walk(x interface{}, context string, visit func(*File, interface{}
232235

233236
// These are ordered and grouped to match ../../pkg/go/ast/ast.go
234237
case *ast.Field:
235-
f.walk(&n.Type, "type", visit)
238+
if len(n.Names) == 0 && context == "field" {
239+
f.walk(&n.Type, "embed-type", visit)
240+
} else {
241+
f.walk(&n.Type, "type", visit)
242+
}
236243
case *ast.FieldList:
237244
for _, field := range n.List {
238245
f.walk(field, context, visit)
@@ -289,9 +296,9 @@ func (f *File) walk(x interface{}, context string, visit func(*File, interface{}
289296
case *ast.StructType:
290297
f.walk(n.Fields, "field", visit)
291298
case *ast.FuncType:
292-
f.walk(n.Params, "field", visit)
299+
f.walk(n.Params, "param", visit)
293300
if n.Results != nil {
294-
f.walk(n.Results, "field", visit)
301+
f.walk(n.Results, "param", visit)
295302
}
296303
case *ast.InterfaceType:
297304
f.walk(n.Methods, "field", visit)
@@ -379,7 +386,7 @@ func (f *File) walk(x interface{}, context string, visit func(*File, interface{}
379386
f.walk(n.Specs, "spec", visit)
380387
case *ast.FuncDecl:
381388
if n.Recv != nil {
382-
f.walk(n.Recv, "field", visit)
389+
f.walk(n.Recv, "param", visit)
383390
}
384391
f.walk(n.Type, "type", visit)
385392
if n.Body != nil {

src/cmd/cgo/doc.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ the pseudo-package "C" and then refers to types such as C.size_t,
1616
variables such as C.stdout, or functions such as C.putchar.
1717
1818
If the import of "C" is immediately preceded by a comment, that
19-
comment is used as a header when compiling the C parts of
20-
the package. For example:
19+
comment, called the preamble, is used as a header when compiling
20+
the C parts of the package. For example:
2121
2222
// #include <stdio.h>
2323
// #include <errno.h>
@@ -57,6 +57,8 @@ The C type void* is represented by Go's unsafe.Pointer.
5757
To access a struct, union, or enum type directly, prefix it with
5858
struct_, union_, or enum_, as in C.struct_stat.
5959
60+
Go structs cannot embed fields with C types.
61+
6062
Any C function that returns a value may be called in a multiple
6163
assignment context to retrieve both the return value and the
6264
C errno variable as an error. For example:
@@ -100,7 +102,8 @@ They will be available in the C code as:
100102
extern int64 MyFunction(int arg1, int arg2, GoString arg3);
101103
extern struct MyFunction2_return MyFunction2(int arg1, int arg2, GoString arg3);
102104
103-
found in _cgo_export.h generated header. Functions with multiple
105+
found in _cgo_export.h generated header, after any preambles
106+
copied from the cgo input files. Functions with multiple
104107
return values are mapped to functions returning a struct.
105108
Not all Go types can be mapped to C types in a useful way.
106109

src/cmd/cgo/gcc.go

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -709,7 +709,7 @@ func (p *Package) rewriteRef(f *File) {
709709
// Substitute definition for mangled type name.
710710
if id, ok := expr.(*ast.Ident); ok {
711711
if t := typedef[id.Name]; t != nil {
712-
expr = t
712+
expr = t.Go
713713
}
714714
if id.Name == r.Name.Mangle && r.Name.Const != "" {
715715
expr = ast.NewIdent(r.Name.Const)
@@ -894,7 +894,7 @@ type typeConv struct {
894894
}
895895

896896
var tagGen int
897-
var typedef = make(map[string]ast.Expr)
897+
var typedef = make(map[string]*Type)
898898
var goIdent = make(map[string]*ast.Ident)
899899

900900
func (c *typeConv) Init(ptrSize int64) {
@@ -1164,17 +1164,22 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
11641164
goIdent[name.Name] = name
11651165
switch dt.Kind {
11661166
case "union", "class":
1167-
typedef[name.Name] = c.Opaque(t.Size)
11681167
if t.C.Empty() {
11691168
t.C.Set("typeof(unsigned char[%d])", t.Size)
11701169
}
1170+
typedef[name.Name] = t
11711171
case "struct":
11721172
g, csyntax, align := c.Struct(dt, pos)
11731173
if t.C.Empty() {
11741174
t.C.Set(csyntax)
11751175
}
11761176
t.Align = align
1177-
typedef[name.Name] = g
1177+
tt := *t
1178+
if tag != "" {
1179+
tt.C = &TypeRepr{"struct %s", []interface{}{tag}}
1180+
}
1181+
tt.Go = g
1182+
typedef[name.Name] = &tt
11781183
}
11791184

11801185
case *dwarf.TypedefType:
@@ -1203,7 +1208,9 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
12031208
t.Size = sub.Size
12041209
t.Align = sub.Align
12051210
if _, ok := typedef[name.Name]; !ok {
1206-
typedef[name.Name] = sub.Go
1211+
tt := *t
1212+
tt.Go = sub.Go
1213+
typedef[name.Name] = &tt
12071214
}
12081215
if *godefs || *cdefs {
12091216
t.Go = sub.Go
@@ -1250,7 +1257,8 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
12501257
}
12511258
s = strings.Join(strings.Split(s, " "), "") // strip spaces
12521259
name := c.Ident("_Ctype_" + s)
1253-
typedef[name.Name] = t.Go
1260+
tt := *t
1261+
typedef[name.Name] = &tt
12541262
if !*godefs && !*cdefs {
12551263
t.Go = name
12561264
}
@@ -1288,9 +1296,18 @@ func (c *typeConv) FuncArg(dtype dwarf.Type, pos token.Pos) *Type {
12881296
if ptr, ok := base(dt.Type).(*dwarf.PtrType); ok {
12891297
// Unless the typedef happens to point to void* since
12901298
// Go has special rules around using unsafe.Pointer.
1291-
if _, void := base(ptr.Type).(*dwarf.VoidType); !void {
1292-
return c.Type(ptr, pos)
1299+
if _, void := base(ptr.Type).(*dwarf.VoidType); void {
1300+
break
12931301
}
1302+
1303+
t = c.Type(ptr, pos)
1304+
if t == nil {
1305+
return nil
1306+
}
1307+
1308+
// Remember the C spelling, in case the struct
1309+
// has __attribute__((unavailable)) on it. See issue 2888.
1310+
t.Typedef = dt.Name
12941311
}
12951312
}
12961313
return t
@@ -1443,7 +1460,7 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct
14431460
off = dt.ByteSize
14441461
}
14451462
if off != dt.ByteSize {
1446-
fatalf("%s: struct size calculation error", lineno(pos))
1463+
fatalf("%s: struct size calculation error off=%d bytesize=%d", lineno(pos), off, dt.ByteSize)
14471464
}
14481465
buf.WriteString("}")
14491466
csyntax = buf.String()

src/cmd/cgo/godefs.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func (p *Package) godefs(f *File, srcfile string) string {
8080
// and xxx is a typedef for yyy, make C.yyy format as T.
8181
for typ, def := range typedef {
8282
if new := override[typ]; new != "" {
83-
if id, ok := def.(*ast.Ident); ok {
83+
if id, ok := def.Go.(*ast.Ident); ok {
8484
override[id.Name] = new
8585
}
8686
}

src/cmd/cgo/main.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ type Package struct {
3939
Decl []ast.Decl
4040
GoFiles []string // list of Go files
4141
GccFiles []string // list of gcc output files
42+
Preamble string // collected preamble for _cgo_export.h
4243
}
4344

4445
// A File collects information about a single Go input file.
@@ -98,6 +99,7 @@ type Type struct {
9899
C *TypeRepr
99100
Go ast.Expr
100101
EnumValues map[string]int64
102+
Typedef string
101103
}
102104

103105
// A FuncType collects information about a function type in both the C and Go worlds.
@@ -312,6 +314,9 @@ func (p *Package) Record(f *File) {
312314
}
313315
}
314316

315-
p.ExpFunc = append(p.ExpFunc, f.ExpFunc...)
317+
if f.ExpFunc != nil {
318+
p.ExpFunc = append(p.ExpFunc, f.ExpFunc...)
319+
p.Preamble += "\n" + f.Preamble
320+
}
316321
p.Decl = append(p.Decl, f.AST.Decls...)
317322
}

src/cmd/cgo/out.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func (p *Package) writeDefs() {
5959

6060
for name, def := range typedef {
6161
fmt.Fprintf(fgo2, "type %s ", name)
62-
conf.Fprint(fgo2, fset, def)
62+
conf.Fprint(fgo2, fset, def.Go)
6363
fmt.Fprintf(fgo2, "\n\n")
6464
}
6565
fmt.Fprintf(fgo2, "type _Ctype_void [0]byte\n")
@@ -196,7 +196,11 @@ func (p *Package) structType(n *Name) (string, int64) {
196196
fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad)
197197
off += pad
198198
}
199-
fmt.Fprintf(&buf, "\t\t%s p%d;\n", t.C, i)
199+
c := t.Typedef
200+
if c == "" {
201+
c = t.C.String()
202+
}
203+
fmt.Fprintf(&buf, "\t\t%s p%d;\n", c, i)
200204
off += t.Size
201205
}
202206
if off%p.PtrSize != 0 {
@@ -428,6 +432,7 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) {
428432
fgcch := creat(*objDir + "_cgo_export.h")
429433

430434
fmt.Fprintf(fgcch, "/* Created by cgo - DO NOT EDIT. */\n")
435+
fmt.Fprintf(fgcch, "%s\n", p.Preamble)
431436
fmt.Fprintf(fgcch, "%s\n", gccExportHeaderProlog)
432437

433438
fmt.Fprintf(fgcc, "/* Created by cgo - DO NOT EDIT. */\n")
@@ -693,10 +698,8 @@ func (p *Package) cgoType(e ast.Expr) *Type {
693698
}
694699
}
695700
}
696-
for name, def := range typedef {
697-
if name == t.Name {
698-
return p.cgoType(def)
699-
}
701+
if def := typedef[t.Name]; def != nil {
702+
return def
700703
}
701704
if t.Name == "uintptr" {
702705
return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("uintptr")}
@@ -721,7 +724,7 @@ func (p *Package) cgoType(e ast.Expr) *Type {
721724
return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("void*")}
722725
}
723726
}
724-
error_(e.Pos(), "unrecognized Go type %T", e)
727+
error_(e.Pos(), "Go type not supported in export: %s", gofmt(e))
725728
return &Type{Size: 4, Align: 4, C: c("int")}
726729
}
727730

src/cmd/cgo/util.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,11 @@ func lineno(pos token.Pos) string {
7070

7171
// Die with an error message.
7272
func fatalf(msg string, args ...interface{}) {
73-
fmt.Fprintf(os.Stderr, msg+"\n", args...)
73+
// If we've already printed other errors, they might have
74+
// caused the fatal condition. Assume they're enough.
75+
if nerrors == 0 {
76+
fmt.Fprintf(os.Stderr, msg+"\n", args...)
77+
}
7478
os.Exit(2)
7579
}
7680

src/pkg/debug/dwarf/testdata/typedef.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,13 @@ typedef struct my_struct {
2828
volatile int vi;
2929
char x : 1;
3030
int y : 4;
31+
int z[0];
3132
long long array[40];
33+
int zz[0];
3234
} t_my_struct;
35+
typedef struct my_struct1 {
36+
int zz [1];
37+
} t_my_struct1;
3338
typedef union my_union {
3439
volatile int vi;
3540
char x : 1;
@@ -65,7 +70,8 @@ t_func_void_of_char *a9;
6570
t_func_void_of_void *a10;
6671
t_func_void_of_ptr_char_dots *a11;
6772
t_my_struct *a12;
68-
t_my_union *a12a;
73+
t_my_struct1 *a12a;
74+
t_my_union *a12b;
6975
t_my_enum *a13;
7076
t_my_list *a14;
7177
t_my_tree *a15;
1.57 KB
Binary file not shown.
-232 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)