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