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