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