Source file
src/cmd/cgo/main.go
1
2
3
4
5
6
7
8
9
10
11 package main
12
13 import (
14 "flag"
15 "fmt"
16 "go/ast"
17 "go/printer"
18 "go/token"
19 "internal/buildcfg"
20 "io"
21 "maps"
22 "os"
23 "path/filepath"
24 "reflect"
25 "runtime"
26 "sort"
27 "strings"
28 "sync"
29
30 "cmd/internal/edit"
31 "cmd/internal/hash"
32 "cmd/internal/objabi"
33 "cmd/internal/par"
34 "cmd/internal/telemetry/counter"
35 )
36
37
38 type Package struct {
39 PackageName string
40 PackagePath string
41 PtrSize int64
42 IntSize int64
43 GccOptions []string
44 GccIsClang bool
45 LdFlags []string
46 Written map[string]bool
47 Name map[string]*Name
48 ExpFunc []*ExpFunc
49 Decl []ast.Decl
50 GoFiles []string
51 GccFiles []string
52 Preamble string
53 noCallbacks map[string]bool
54 noEscapes map[string]bool
55 }
56
57
58
59 type typedefInfo struct {
60 typedef string
61 pos token.Pos
62 }
63
64
65 type File struct {
66 AST *ast.File
67 Comments []*ast.CommentGroup
68 Package string
69 Preamble string
70 Ref []*Ref
71 Calls []*Call
72 ExpFunc []*ExpFunc
73 Name map[string]*Name
74 NamePos map[*Name]token.Pos
75 NoCallbacks map[string]bool
76 NoEscapes map[string]bool
77 Edit *edit.Buffer
78
79 debugs []*debug
80 }
81
82 func (f *File) offset(p token.Pos) int {
83 return fset.Position(p).Offset
84 }
85
86 func nameKeys(m map[string]*Name) []string {
87 ks := make([]string, 0, len(m))
88 for k := range m {
89 ks = append(ks, k)
90 }
91 sort.Strings(ks)
92 return ks
93 }
94
95
96 type Call struct {
97 Call *ast.CallExpr
98 Deferred bool
99 Done bool
100 }
101
102
103 type Ref struct {
104 Name *Name
105 Expr *ast.Expr
106 Context astContext
107 Done bool
108 }
109
110 func (r *Ref) Pos() token.Pos {
111 return (*r.Expr).Pos()
112 }
113
114 var nameKinds = []string{"iconst", "fconst", "sconst", "type", "var", "fpvar", "func", "macro", "not-type"}
115
116
117 type Name struct {
118 Go string
119 Mangle string
120 C string
121 Define string
122 Kind string
123 Type *Type
124 FuncType *FuncType
125 AddError bool
126 Const string
127 }
128
129
130 func (n *Name) IsVar() bool {
131 return n.Kind == "var" || n.Kind == "fpvar"
132 }
133
134
135 func (n *Name) IsConst() bool {
136 return strings.HasSuffix(n.Kind, "const")
137 }
138
139
140
141
142 type ExpFunc struct {
143 Func *ast.FuncDecl
144 ExpName string
145 Doc string
146 }
147
148
149 type TypeRepr struct {
150 Repr string
151 FormatArgs []any
152 }
153
154
155 type Type struct {
156 Size int64
157 Align int64
158 C *TypeRepr
159 Go ast.Expr
160 EnumValues map[string]int64
161 Typedef string
162 BadPointer bool
163 }
164
165 func (t *Type) fuzzyMatch(t2 *Type) bool {
166 if t == nil || t2 == nil {
167 return false
168 }
169 return t.Size == t2.Size && t.Align == t2.Align
170 }
171
172
173 type FuncType struct {
174 Params []*Type
175 Result *Type
176 Go *ast.FuncType
177 }
178
179 func (t *FuncType) fuzzyMatch(t2 *FuncType) bool {
180 if t == nil || t2 == nil {
181 return false
182 }
183 if !t.Result.fuzzyMatch(t2.Result) {
184 return false
185 }
186 if len(t.Params) != len(t2.Params) {
187 return false
188 }
189 for i := range t.Params {
190 if !t.Params[i].fuzzyMatch(t2.Params[i]) {
191 return false
192 }
193 }
194 return true
195 }
196
197 func usage() {
198 fmt.Fprint(os.Stderr, "usage: cgo -- [compiler options] file.go ...\n")
199 flag.PrintDefaults()
200 os.Exit(2)
201 }
202
203 var ptrSizeMap = map[string]int64{
204 "386": 4,
205 "alpha": 8,
206 "amd64": 8,
207 "arm": 4,
208 "arm64": 8,
209 "loong64": 8,
210 "m68k": 4,
211 "mips": 4,
212 "mipsle": 4,
213 "mips64": 8,
214 "mips64le": 8,
215 "nios2": 4,
216 "ppc": 4,
217 "ppc64": 8,
218 "ppc64le": 8,
219 "riscv": 4,
220 "riscv64": 8,
221 "s390": 4,
222 "s390x": 8,
223 "sh": 4,
224 "shbe": 4,
225 "sparc": 4,
226 "sparc64": 8,
227 }
228
229 var intSizeMap = map[string]int64{
230 "386": 4,
231 "alpha": 8,
232 "amd64": 8,
233 "arm": 4,
234 "arm64": 8,
235 "loong64": 8,
236 "m68k": 4,
237 "mips": 4,
238 "mipsle": 4,
239 "mips64": 8,
240 "mips64le": 8,
241 "nios2": 4,
242 "ppc": 4,
243 "ppc64": 8,
244 "ppc64le": 8,
245 "riscv": 4,
246 "riscv64": 8,
247 "s390": 4,
248 "s390x": 8,
249 "sh": 4,
250 "shbe": 4,
251 "sparc": 4,
252 "sparc64": 8,
253 }
254
255 var cPrefix string
256
257 var fset = token.NewFileSet()
258
259 var dynobj = flag.String("dynimport", "", "if non-empty, print dynamic import data for that file")
260 var dynout = flag.String("dynout", "", "write -dynimport output to this file")
261 var dynpackage = flag.String("dynpackage", "main", "set Go package for -dynimport output")
262 var dynlinker = flag.Bool("dynlinker", false, "record dynamic linker information in -dynimport mode")
263
264
265
266
267 var godefs = flag.Bool("godefs", false, "for bootstrap: write Go definitions for C file to standard output")
268
269 var srcDir = flag.String("srcdir", "", "source directory")
270 var objDir = flag.String("objdir", "", "object directory")
271 var importPath = flag.String("importpath", "", "import path of package being built (for comments in generated files)")
272 var exportHeader = flag.String("exportheader", "", "where to write export header if any exported functions")
273
274 var ldflags = flag.String("ldflags", "", "flags to pass to C linker")
275
276 var gccgo = flag.Bool("gccgo", false, "generate files for use with gccgo")
277 var gccgoprefix = flag.String("gccgoprefix", "", "-fgo-prefix option used with gccgo")
278 var gccgopkgpath = flag.String("gccgopkgpath", "", "-fgo-pkgpath option used with gccgo")
279 var gccgoMangler func(string) string
280 var gccgoDefineCgoIncomplete = flag.Bool("gccgo_define_cgoincomplete", false, "define cgo.Incomplete for older gccgo/GoLLVM")
281 var importRuntimeCgo = flag.Bool("import_runtime_cgo", true, "import runtime/cgo in generated code")
282 var importSyscall = flag.Bool("import_syscall", true, "import syscall in generated code")
283 var trimpath = flag.String("trimpath", "", "applies supplied rewrites or trims prefixes to recorded source file paths")
284
285 var goarch, goos, gomips, gomips64 string
286 var gccBaseCmd []string
287
288 func main() {
289 counter.Open()
290 objabi.AddVersionFlag()
291 objabi.Flagparse(usage)
292 counter.Inc("cgo/invocations")
293 counter.CountFlags("cgo/flag:", *flag.CommandLine)
294
295 if *gccgoDefineCgoIncomplete {
296 if !*gccgo {
297 fmt.Fprintf(os.Stderr, "cgo: -gccgo_define_cgoincomplete without -gccgo\n")
298 os.Exit(2)
299 }
300 incomplete = "_cgopackage_Incomplete"
301 }
302
303 if *dynobj != "" {
304
305
306
307
308
309
310
311
312 dynimport(*dynobj)
313 return
314 }
315
316 if *godefs {
317
318
319
320 conf.Mode &^= printer.SourcePos
321 }
322
323 if *objDir == "" {
324 *objDir = "_obj"
325 }
326
327 args := flag.Args()
328 if len(args) < 1 {
329 usage()
330 }
331
332
333
334 var i int
335 for i = len(args); i > 0; i-- {
336 if !strings.HasSuffix(args[i-1], ".go") {
337 break
338 }
339 }
340 if i == len(args) {
341 usage()
342 }
343
344
345
346 osArgs := make([]string, len(os.Args))
347 copy(osArgs, os.Args[:])
348 goFiles := args[i:]
349
350 for _, arg := range args[:i] {
351 if arg == "-fsanitize=thread" {
352 tsanProlog = yesTsanProlog
353 }
354 if arg == "-fsanitize=memory" {
355 msanProlog = yesMsanProlog
356 }
357 }
358
359 p := newPackage(args[:i])
360
361
362 var err error
363 gccBaseCmd, err = checkGCCBaseCmd()
364 if err != nil {
365 fatalf("%v", err)
366 os.Exit(2)
367 }
368
369
370 if *ldflags != "" {
371 args, err := splitQuoted(*ldflags)
372 if err != nil {
373 fatalf("bad -ldflags option: %q (%s)", *ldflags, err)
374 }
375 p.addToFlag("LDFLAGS", args)
376 }
377
378
379
380
381
382
383
384
385 if envFlags := os.Getenv("CGO_LDFLAGS"); envFlags != "" {
386 args, err := splitQuoted(envFlags)
387 if err != nil {
388 fatalf("bad CGO_LDFLAGS: %q (%s)", envFlags, err)
389 }
390 p.addToFlag("LDFLAGS", args)
391 }
392
393
394
395
396
397
398 h := hash.New32()
399 io.WriteString(h, *importPath)
400 var once sync.Once
401 q := par.NewQueue(runtime.GOMAXPROCS(0))
402 fs := make([]*File, len(goFiles))
403 for i, input := range goFiles {
404 if *srcDir != "" {
405 input = filepath.Join(*srcDir, input)
406 }
407
408
409
410
411 if aname, err := filepath.Abs(input); err == nil {
412 input = aname
413 }
414
415 b, err := os.ReadFile(input)
416 if err != nil {
417 fatalf("%s", err)
418 }
419 if _, err = h.Write(b); err != nil {
420 fatalf("%s", err)
421 }
422
423 q.Add(func() {
424
425 input, _ = objabi.ApplyRewrites(input, *trimpath)
426 if strings.ContainsAny(input, "\r\n") {
427
428
429
430 fatalf("input path contains newline character: %q", input)
431 }
432 goFiles[i] = input
433
434 f := new(File)
435 f.Edit = edit.NewBuffer(b)
436 f.ParseGo(input, b)
437 f.ProcessCgoDirectives()
438 gccIsClang := f.loadDefines(p.GccOptions)
439 once.Do(func() {
440 p.GccIsClang = gccIsClang
441 })
442
443 fs[i] = f
444
445 f.loadDebug(p)
446 })
447 }
448
449 <-q.Idle()
450
451 cPrefix = fmt.Sprintf("_%x", h.Sum(nil)[0:6])
452
453 for i, input := range goFiles {
454 f := fs[i]
455 p.Translate(f)
456 for _, cref := range f.Ref {
457 switch cref.Context {
458 case ctxCall, ctxCall2:
459 if cref.Name.Kind != "type" {
460 break
461 }
462 old := *cref.Expr
463 *cref.Expr = cref.Name.Type.Go
464 f.Edit.Replace(f.offset(old.Pos()), f.offset(old.End()), gofmt(cref.Name.Type.Go))
465 }
466 }
467 if nerrors > 0 {
468 os.Exit(2)
469 }
470 p.PackagePath = f.Package
471 p.Record(f)
472 if *godefs {
473 os.Stdout.WriteString(p.godefs(f, osArgs))
474 } else {
475 p.writeOutput(f, input)
476 }
477 }
478 cFunctions := make(map[string]bool)
479 for _, key := range nameKeys(p.Name) {
480 n := p.Name[key]
481 if n.FuncType != nil {
482 cFunctions[n.C] = true
483 }
484 }
485
486 for funcName := range p.noEscapes {
487 if _, found := cFunctions[funcName]; !found {
488 error_(token.NoPos, "#cgo noescape %s: no matched C function", funcName)
489 }
490 }
491
492 for funcName := range p.noCallbacks {
493 if _, found := cFunctions[funcName]; !found {
494 error_(token.NoPos, "#cgo nocallback %s: no matched C function", funcName)
495 }
496 }
497
498 if !*godefs {
499 p.writeDefs()
500 }
501 if nerrors > 0 {
502 os.Exit(2)
503 }
504 }
505
506
507
508 func newPackage(args []string) *Package {
509 goarch = runtime.GOARCH
510 if s := os.Getenv("GOARCH"); s != "" {
511 goarch = s
512 }
513 goos = runtime.GOOS
514 if s := os.Getenv("GOOS"); s != "" {
515 goos = s
516 }
517 buildcfg.Check()
518 gomips = buildcfg.GOMIPS
519 gomips64 = buildcfg.GOMIPS64
520 ptrSize := ptrSizeMap[goarch]
521 if ptrSize == 0 {
522 fatalf("unknown ptrSize for $GOARCH %q", goarch)
523 }
524 intSize := intSizeMap[goarch]
525 if intSize == 0 {
526 fatalf("unknown intSize for $GOARCH %q", goarch)
527 }
528
529
530 os.Setenv("LANG", "en_US.UTF-8")
531 os.Setenv("LC_ALL", "C")
532
533 p := &Package{
534 PtrSize: ptrSize,
535 IntSize: intSize,
536 Written: make(map[string]bool),
537 noCallbacks: make(map[string]bool),
538 noEscapes: make(map[string]bool),
539 }
540 p.addToFlag("CFLAGS", args)
541 return p
542 }
543
544
545 func (p *Package) Record(f *File) {
546 if p.PackageName == "" {
547 p.PackageName = f.Package
548 } else if p.PackageName != f.Package {
549 error_(token.NoPos, "inconsistent package names: %s, %s", p.PackageName, f.Package)
550 }
551
552 if p.Name == nil {
553 p.Name = f.Name
554 } else {
555
556 for k, v := range f.Name {
557 if p.Name[k] == nil {
558
559 p.Name[k] = v
560 } else if p.incompleteTypedef(p.Name[k].Type) && p.Name[k].FuncType == nil {
561
562 p.Name[k] = v
563 } else if p.incompleteTypedef(v.Type) && v.FuncType == nil {
564
565
566 } else if _, ok := nameToC[k]; ok {
567
568
569
570 } else if !reflect.DeepEqual(p.Name[k], v) {
571
572
573
574
575
576
577
578 ok := false
579 ft1 := p.Name[k].FuncType
580 ft2 := v.FuncType
581 if ft1.fuzzyMatch(ft2) {
582
583 x1 := *p.Name[k]
584 x2 := *v
585 x1.FuncType = nil
586 x2.FuncType = nil
587 if reflect.DeepEqual(&x1, &x2) {
588 ok = true
589 }
590 }
591 if !ok {
592 error_(token.NoPos, "inconsistent definitions for C.%s", fixGo(k))
593 }
594 }
595 }
596 }
597
598
599 maps.Copy(p.noCallbacks, f.NoCallbacks)
600 maps.Copy(p.noEscapes, f.NoEscapes)
601
602 if f.ExpFunc != nil {
603 p.ExpFunc = append(p.ExpFunc, f.ExpFunc...)
604 p.Preamble += "\n" + f.Preamble
605 }
606 p.Decl = append(p.Decl, f.AST.Decls...)
607 }
608
609
610
611 func (p *Package) incompleteTypedef(t *Type) bool {
612 return t == nil || (t.Size == 0 && t.Align == -1)
613 }
614
View as plain text