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