Source file
src/cmd/cgo/main.go
Documentation: cmd/cgo
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
163 type FuncType struct {
164 Params []*Type
165 Result *Type
166 Go *ast.FuncType
167 }
168
169 func usage() {
170 fmt.Fprint(os.Stderr, "usage: cgo -- [compiler options] file.go ...\n")
171 flag.PrintDefaults()
172 os.Exit(2)
173 }
174
175 var ptrSizeMap = map[string]int64{
176 "386": 4,
177 "alpha": 8,
178 "amd64": 8,
179 "arm": 4,
180 "arm64": 8,
181 "loong64": 8,
182 "m68k": 4,
183 "mips": 4,
184 "mipsle": 4,
185 "mips64": 8,
186 "mips64le": 8,
187 "nios2": 4,
188 "ppc": 4,
189 "ppc64": 8,
190 "ppc64le": 8,
191 "riscv": 4,
192 "riscv64": 8,
193 "s390": 4,
194 "s390x": 8,
195 "sh": 4,
196 "shbe": 4,
197 "sparc": 4,
198 "sparc64": 8,
199 }
200
201 var intSizeMap = map[string]int64{
202 "386": 4,
203 "alpha": 8,
204 "amd64": 8,
205 "arm": 4,
206 "arm64": 8,
207 "loong64": 8,
208 "m68k": 4,
209 "mips": 4,
210 "mipsle": 4,
211 "mips64": 8,
212 "mips64le": 8,
213 "nios2": 4,
214 "ppc": 4,
215 "ppc64": 8,
216 "ppc64le": 8,
217 "riscv": 4,
218 "riscv64": 8,
219 "s390": 4,
220 "s390x": 8,
221 "sh": 4,
222 "shbe": 4,
223 "sparc": 4,
224 "sparc64": 8,
225 }
226
227 var cPrefix string
228
229 var fset = token.NewFileSet()
230
231 var dynobj = flag.String("dynimport", "", "if non-empty, print dynamic import data for that file")
232 var dynout = flag.String("dynout", "", "write -dynimport output to this file")
233 var dynpackage = flag.String("dynpackage", "main", "set Go package for -dynimport output")
234 var dynlinker = flag.Bool("dynlinker", false, "record dynamic linker information in -dynimport mode")
235
236
237
238
239 var godefs = flag.Bool("godefs", false, "for bootstrap: write Go definitions for C file to standard output")
240
241 var srcDir = flag.String("srcdir", "", "source directory")
242 var objDir = flag.String("objdir", "", "object directory")
243 var importPath = flag.String("importpath", "", "import path of package being built (for comments in generated files)")
244 var exportHeader = flag.String("exportheader", "", "where to write export header if any exported functions")
245
246 var ldflags = flag.String("ldflags", "", "flags to pass to C linker")
247
248 var gccgo = flag.Bool("gccgo", false, "generate files for use with gccgo")
249 var gccgoprefix = flag.String("gccgoprefix", "", "-fgo-prefix option used with gccgo")
250 var gccgopkgpath = flag.String("gccgopkgpath", "", "-fgo-pkgpath option used with gccgo")
251 var gccgoMangler func(string) string
252 var gccgoDefineCgoIncomplete = flag.Bool("gccgo_define_cgoincomplete", false, "define cgo.Incomplete for older gccgo/GoLLVM")
253 var importRuntimeCgo = flag.Bool("import_runtime_cgo", true, "import runtime/cgo in generated code")
254 var importSyscall = flag.Bool("import_syscall", true, "import syscall in generated code")
255 var trimpath = flag.String("trimpath", "", "applies supplied rewrites or trims prefixes to recorded source file paths")
256
257 var goarch, goos, gomips, gomips64 string
258 var gccBaseCmd []string
259
260 func main() {
261 counter.Open()
262 objabi.AddVersionFlag()
263 objabi.Flagparse(usage)
264 counter.Inc("cgo/invocations")
265 counter.CountFlags("cgo/flag:", *flag.CommandLine)
266
267 if *gccgoDefineCgoIncomplete {
268 if !*gccgo {
269 fmt.Fprintf(os.Stderr, "cgo: -gccgo_define_cgoincomplete without -gccgo\n")
270 os.Exit(2)
271 }
272 incomplete = "_cgopackage_Incomplete"
273 }
274
275 if *dynobj != "" {
276
277
278
279
280
281
282
283
284 dynimport(*dynobj)
285 return
286 }
287
288 if *godefs {
289
290
291
292 conf.Mode &^= printer.SourcePos
293 }
294
295 args := flag.Args()
296 if len(args) < 1 {
297 usage()
298 }
299
300
301
302 var i int
303 for i = len(args); i > 0; i-- {
304 if !strings.HasSuffix(args[i-1], ".go") {
305 break
306 }
307 }
308 if i == len(args) {
309 usage()
310 }
311
312
313
314 osArgs := make([]string, len(os.Args))
315 copy(osArgs, os.Args[:])
316 goFiles := args[i:]
317
318 for _, arg := range args[:i] {
319 if arg == "-fsanitize=thread" {
320 tsanProlog = yesTsanProlog
321 }
322 if arg == "-fsanitize=memory" {
323 msanProlog = yesMsanProlog
324 }
325 }
326
327 p := newPackage(args[:i])
328
329
330 var err error
331 gccBaseCmd, err = checkGCCBaseCmd()
332 if err != nil {
333 fatalf("%v", err)
334 os.Exit(2)
335 }
336
337
338 if *ldflags != "" {
339 args, err := splitQuoted(*ldflags)
340 if err != nil {
341 fatalf("bad -ldflags option: %q (%s)", *ldflags, err)
342 }
343 p.addToFlag("LDFLAGS", args)
344 }
345
346
347
348
349
350
351
352
353 if envFlags := os.Getenv("CGO_LDFLAGS"); envFlags != "" {
354 args, err := splitQuoted(envFlags)
355 if err != nil {
356 fatalf("bad CGO_LDFLAGS: %q (%s)", envFlags, err)
357 }
358 p.addToFlag("LDFLAGS", args)
359 }
360
361
362
363
364
365
366 h := notsha256.New()
367 io.WriteString(h, *importPath)
368 fs := make([]*File, len(goFiles))
369 for i, input := range goFiles {
370 if *srcDir != "" {
371 input = filepath.Join(*srcDir, input)
372 }
373
374
375
376
377 if aname, err := filepath.Abs(input); err == nil {
378 input = aname
379 }
380
381 b, err := os.ReadFile(input)
382 if err != nil {
383 fatalf("%s", err)
384 }
385 if _, err = h.Write(b); err != nil {
386 fatalf("%s", err)
387 }
388
389
390 input, _ = objabi.ApplyRewrites(input, *trimpath)
391 if strings.ContainsAny(input, "\r\n") {
392
393
394
395 fatalf("input path contains newline character: %q", input)
396 }
397 goFiles[i] = input
398
399 f := new(File)
400 f.Edit = edit.NewBuffer(b)
401 f.ParseGo(input, b)
402 f.ProcessCgoDirectives()
403 fs[i] = f
404 }
405
406 cPrefix = fmt.Sprintf("_%x", h.Sum(nil)[0:6])
407
408 if *objDir == "" {
409 *objDir = "_obj"
410 }
411
412
413 os.MkdirAll(*objDir, 0o700)
414 *objDir += string(filepath.Separator)
415
416 for i, input := range goFiles {
417 f := fs[i]
418 p.Translate(f)
419 for _, cref := range f.Ref {
420 switch cref.Context {
421 case ctxCall, ctxCall2:
422 if cref.Name.Kind != "type" {
423 break
424 }
425 old := *cref.Expr
426 *cref.Expr = cref.Name.Type.Go
427 f.Edit.Replace(f.offset(old.Pos()), f.offset(old.End()), gofmt(cref.Name.Type.Go))
428 }
429 }
430 if nerrors > 0 {
431 os.Exit(2)
432 }
433 p.PackagePath = f.Package
434 p.Record(f)
435 if *godefs {
436 os.Stdout.WriteString(p.godefs(f, osArgs))
437 } else {
438 p.writeOutput(f, input)
439 }
440 }
441 cFunctions := make(map[string]bool)
442 for _, key := range nameKeys(p.Name) {
443 n := p.Name[key]
444 if n.FuncType != nil {
445 cFunctions[n.C] = true
446 }
447 }
448
449 for funcName := range p.noEscapes {
450 if _, found := cFunctions[funcName]; !found {
451 error_(token.NoPos, "#cgo noescape %s: no matched C function", funcName)
452 }
453 }
454
455 for funcName := range p.noCallbacks {
456 if _, found := cFunctions[funcName]; !found {
457 error_(token.NoPos, "#cgo nocallback %s: no matched C function", funcName)
458 }
459 }
460
461 if !*godefs {
462 p.writeDefs()
463 }
464 if nerrors > 0 {
465 os.Exit(2)
466 }
467 }
468
469
470
471 func newPackage(args []string) *Package {
472 goarch = runtime.GOARCH
473 if s := os.Getenv("GOARCH"); s != "" {
474 goarch = s
475 }
476 goos = runtime.GOOS
477 if s := os.Getenv("GOOS"); s != "" {
478 goos = s
479 }
480 buildcfg.Check()
481 gomips = buildcfg.GOMIPS
482 gomips64 = buildcfg.GOMIPS64
483 ptrSize := ptrSizeMap[goarch]
484 if ptrSize == 0 {
485 fatalf("unknown ptrSize for $GOARCH %q", goarch)
486 }
487 intSize := intSizeMap[goarch]
488 if intSize == 0 {
489 fatalf("unknown intSize for $GOARCH %q", goarch)
490 }
491
492
493 os.Setenv("LANG", "en_US.UTF-8")
494 os.Setenv("LC_ALL", "C")
495
496 p := &Package{
497 PtrSize: ptrSize,
498 IntSize: intSize,
499 Written: make(map[string]bool),
500 noCallbacks: make(map[string]bool),
501 noEscapes: make(map[string]bool),
502 }
503 p.addToFlag("CFLAGS", args)
504 return p
505 }
506
507
508 func (p *Package) Record(f *File) {
509 if p.PackageName == "" {
510 p.PackageName = f.Package
511 } else if p.PackageName != f.Package {
512 error_(token.NoPos, "inconsistent package names: %s, %s", p.PackageName, f.Package)
513 }
514
515 if p.Name == nil {
516 p.Name = f.Name
517 } else {
518 for k, v := range f.Name {
519 if p.Name[k] == nil {
520 p.Name[k] = v
521 } else if p.incompleteTypedef(p.Name[k].Type) {
522 p.Name[k] = v
523 } else if p.incompleteTypedef(v.Type) {
524
525 } else if _, ok := nameToC[k]; ok {
526
527
528
529 } else if !reflect.DeepEqual(p.Name[k], v) {
530 error_(token.NoPos, "inconsistent definitions for C.%s", fixGo(k))
531 }
532 }
533 }
534
535
536 for k, v := range f.NoCallbacks {
537 p.noCallbacks[k] = v
538 }
539 for k, v := range f.NoEscapes {
540 p.noEscapes[k] = v
541 }
542
543 if f.ExpFunc != nil {
544 p.ExpFunc = append(p.ExpFunc, f.ExpFunc...)
545 p.Preamble += "\n" + f.Preamble
546 }
547 p.Decl = append(p.Decl, f.AST.Decls...)
548 }
549
550
551
552 func (p *Package) incompleteTypedef(t *Type) bool {
553 return t == nil || (t.Size == 0 && t.Align == -1)
554 }
555
View as plain text