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