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 []interface{}
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 args := flag.Args()
324 if len(args) < 1 {
325 usage()
326 }
327
328
329
330 var i int
331 for i = len(args); i > 0; i-- {
332 if !strings.HasSuffix(args[i-1], ".go") {
333 break
334 }
335 }
336 if i == len(args) {
337 usage()
338 }
339
340
341
342 osArgs := make([]string, len(os.Args))
343 copy(osArgs, os.Args[:])
344 goFiles := args[i:]
345
346 for _, arg := range args[:i] {
347 if arg == "-fsanitize=thread" {
348 tsanProlog = yesTsanProlog
349 }
350 if arg == "-fsanitize=memory" {
351 msanProlog = yesMsanProlog
352 }
353 }
354
355 p := newPackage(args[:i])
356
357
358 var err error
359 gccBaseCmd, err = checkGCCBaseCmd()
360 if err != nil {
361 fatalf("%v", err)
362 os.Exit(2)
363 }
364
365
366 if *ldflags != "" {
367 args, err := splitQuoted(*ldflags)
368 if err != nil {
369 fatalf("bad -ldflags option: %q (%s)", *ldflags, err)
370 }
371 p.addToFlag("LDFLAGS", args)
372 }
373
374
375
376
377
378
379
380
381 if envFlags := os.Getenv("CGO_LDFLAGS"); envFlags != "" {
382 args, err := splitQuoted(envFlags)
383 if err != nil {
384 fatalf("bad CGO_LDFLAGS: %q (%s)", envFlags, err)
385 }
386 p.addToFlag("LDFLAGS", args)
387 }
388
389
390
391
392
393
394 h := hash.New32()
395 io.WriteString(h, *importPath)
396 var once sync.Once
397 q := par.NewQueue(runtime.GOMAXPROCS(0))
398 fs := make([]*File, len(goFiles))
399 for i, input := range goFiles {
400 if *srcDir != "" {
401 input = filepath.Join(*srcDir, input)
402 }
403
404
405
406
407 if aname, err := filepath.Abs(input); err == nil {
408 input = aname
409 }
410
411 b, err := os.ReadFile(input)
412 if err != nil {
413 fatalf("%s", err)
414 }
415 if _, err = h.Write(b); err != nil {
416 fatalf("%s", err)
417 }
418
419 q.Add(func() {
420
421 input, _ = objabi.ApplyRewrites(input, *trimpath)
422 if strings.ContainsAny(input, "\r\n") {
423
424
425
426 fatalf("input path contains newline character: %q", input)
427 }
428 goFiles[i] = input
429
430 f := new(File)
431 f.Edit = edit.NewBuffer(b)
432 f.ParseGo(input, b)
433 f.ProcessCgoDirectives()
434 gccIsClang := f.loadDefines(p.GccOptions)
435 once.Do(func() {
436 p.GccIsClang = gccIsClang
437 })
438
439 fs[i] = f
440
441 f.loadDebug(p)
442 })
443 }
444
445 <-q.Idle()
446
447 cPrefix = fmt.Sprintf("_%x", h.Sum(nil)[0:6])
448
449 if *objDir == "" {
450 *objDir = "_obj"
451 }
452
453
454 os.MkdirAll(*objDir, 0o700)
455 *objDir += string(filepath.Separator)
456
457 for i, input := range goFiles {
458 f := fs[i]
459 p.Translate(f)
460 for _, cref := range f.Ref {
461 switch cref.Context {
462 case ctxCall, ctxCall2:
463 if cref.Name.Kind != "type" {
464 break
465 }
466 old := *cref.Expr
467 *cref.Expr = cref.Name.Type.Go
468 f.Edit.Replace(f.offset(old.Pos()), f.offset(old.End()), gofmt(cref.Name.Type.Go))
469 }
470 }
471 if nerrors > 0 {
472 os.Exit(2)
473 }
474 p.PackagePath = f.Package
475 p.Record(f)
476 if *godefs {
477 os.Stdout.WriteString(p.godefs(f, osArgs))
478 } else {
479 p.writeOutput(f, input)
480 }
481 }
482 cFunctions := make(map[string]bool)
483 for _, key := range nameKeys(p.Name) {
484 n := p.Name[key]
485 if n.FuncType != nil {
486 cFunctions[n.C] = true
487 }
488 }
489
490 for funcName := range p.noEscapes {
491 if _, found := cFunctions[funcName]; !found {
492 error_(token.NoPos, "#cgo noescape %s: no matched C function", funcName)
493 }
494 }
495
496 for funcName := range p.noCallbacks {
497 if _, found := cFunctions[funcName]; !found {
498 error_(token.NoPos, "#cgo nocallback %s: no matched C function", funcName)
499 }
500 }
501
502 if !*godefs {
503 p.writeDefs()
504 }
505 if nerrors > 0 {
506 os.Exit(2)
507 }
508 }
509
510
511
512 func newPackage(args []string) *Package {
513 goarch = runtime.GOARCH
514 if s := os.Getenv("GOARCH"); s != "" {
515 goarch = s
516 }
517 goos = runtime.GOOS
518 if s := os.Getenv("GOOS"); s != "" {
519 goos = s
520 }
521 buildcfg.Check()
522 gomips = buildcfg.GOMIPS
523 gomips64 = buildcfg.GOMIPS64
524 ptrSize := ptrSizeMap[goarch]
525 if ptrSize == 0 {
526 fatalf("unknown ptrSize for $GOARCH %q", goarch)
527 }
528 intSize := intSizeMap[goarch]
529 if intSize == 0 {
530 fatalf("unknown intSize for $GOARCH %q", goarch)
531 }
532
533
534 os.Setenv("LANG", "en_US.UTF-8")
535 os.Setenv("LC_ALL", "C")
536
537 p := &Package{
538 PtrSize: ptrSize,
539 IntSize: intSize,
540 Written: make(map[string]bool),
541 noCallbacks: make(map[string]bool),
542 noEscapes: make(map[string]bool),
543 }
544 p.addToFlag("CFLAGS", args)
545 return p
546 }
547
548
549 func (p *Package) Record(f *File) {
550 if p.PackageName == "" {
551 p.PackageName = f.Package
552 } else if p.PackageName != f.Package {
553 error_(token.NoPos, "inconsistent package names: %s, %s", p.PackageName, f.Package)
554 }
555
556 if p.Name == nil {
557 p.Name = f.Name
558 } else {
559
560 for k, v := range f.Name {
561 if p.Name[k] == nil {
562
563 p.Name[k] = v
564 } else if p.incompleteTypedef(p.Name[k].Type) && p.Name[k].FuncType == nil {
565
566 p.Name[k] = v
567 } else if p.incompleteTypedef(v.Type) && v.FuncType == nil {
568
569
570 } else if _, ok := nameToC[k]; ok {
571
572
573
574 } else if !reflect.DeepEqual(p.Name[k], v) {
575
576
577
578
579
580
581
582 ok := false
583 ft1 := p.Name[k].FuncType
584 ft2 := v.FuncType
585 if ft1.fuzzyMatch(ft2) {
586
587 x1 := *p.Name[k]
588 x2 := *v
589 x1.FuncType = nil
590 x2.FuncType = nil
591 if reflect.DeepEqual(&x1, &x2) {
592 ok = true
593 }
594 }
595 if !ok {
596 error_(token.NoPos, "inconsistent definitions for C.%s", fixGo(k))
597 }
598 }
599 }
600 }
601
602
603 maps.Copy(p.noCallbacks, f.NoCallbacks)
604 maps.Copy(p.noEscapes, f.NoEscapes)
605
606 if f.ExpFunc != nil {
607 p.ExpFunc = append(p.ExpFunc, f.ExpFunc...)
608 p.Preamble += "\n" + f.Preamble
609 }
610 p.Decl = append(p.Decl, f.AST.Decls...)
611 }
612
613
614
615 func (p *Package) incompleteTypedef(t *Type) bool {
616 return t == nil || (t.Size == 0 && t.Align == -1)
617 }
618
View as plain text