]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/cgo/main.go
all: REVERSE MERGE dev.boringcrypto (cdcb4b6) into master
[gostls13.git] / src / cmd / cgo / main.go
1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // Cgo; see doc.go for an overview.
6
7 // TODO(rsc):
8 //      Emit correct line number annotations.
9 //      Make gc understand the annotations.
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         "io/ioutil"
22         "os"
23         "path/filepath"
24         "reflect"
25         "runtime"
26         "sort"
27         "strings"
28
29         "cmd/internal/edit"
30         "cmd/internal/notsha256"
31         "cmd/internal/objabi"
32 )
33
34 // A Package collects information about the package we're going to write.
35 type Package struct {
36         PackageName string // name of package
37         PackagePath string
38         PtrSize     int64
39         IntSize     int64
40         GccOptions  []string
41         GccIsClang  bool
42         CgoFlags    map[string][]string // #cgo flags (CFLAGS, LDFLAGS)
43         Written     map[string]bool
44         Name        map[string]*Name // accumulated Name from Files
45         ExpFunc     []*ExpFunc       // accumulated ExpFunc from Files
46         Decl        []ast.Decl
47         GoFiles     []string        // list of Go files
48         GccFiles    []string        // list of gcc output files
49         Preamble    string          // collected preamble for _cgo_export.h
50         typedefs    map[string]bool // type names that appear in the types of the objects we're interested in
51         typedefList []typedefInfo
52 }
53
54 // A typedefInfo is an element on Package.typedefList: a typedef name
55 // and the position where it was required.
56 type typedefInfo struct {
57         typedef string
58         pos     token.Pos
59 }
60
61 // A File collects information about a single Go input file.
62 type File struct {
63         AST      *ast.File           // parsed AST
64         Comments []*ast.CommentGroup // comments from file
65         Package  string              // Package name
66         Preamble string              // C preamble (doc comment on import "C")
67         Ref      []*Ref              // all references to C.xxx in AST
68         Calls    []*Call             // all calls to C.xxx in AST
69         ExpFunc  []*ExpFunc          // exported functions for this file
70         Name     map[string]*Name    // map from Go name to Name
71         NamePos  map[*Name]token.Pos // map from Name to position of the first reference
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 // A Call refers to a call of a C.xxx function in the AST.
89 type Call struct {
90         Call     *ast.CallExpr
91         Deferred bool
92         Done     bool
93 }
94
95 // A Ref refers to an expression of the form C.xxx in the AST.
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 // A Name collects information about C.xxx.
110 type Name struct {
111         Go       string // name used in Go referring to package C
112         Mangle   string // name used in generated Go
113         C        string // name used in C
114         Define   string // #define expansion
115         Kind     string // one of the nameKinds
116         Type     *Type  // the type of xxx
117         FuncType *FuncType
118         AddError bool
119         Const    string // constant definition
120 }
121
122 // IsVar reports whether Kind is either "var" or "fpvar"
123 func (n *Name) IsVar() bool {
124         return n.Kind == "var" || n.Kind == "fpvar"
125 }
126
127 // IsConst reports whether Kind is either "iconst", "fconst" or "sconst"
128 func (n *Name) IsConst() bool {
129         return strings.HasSuffix(n.Kind, "const")
130 }
131
132 // An ExpFunc is an exported function, callable from C.
133 // Such functions are identified in the Go input file
134 // by doc comments containing the line //export ExpName
135 type ExpFunc struct {
136         Func    *ast.FuncDecl
137         ExpName string // name to use from C
138         Doc     string
139 }
140
141 // A TypeRepr contains the string representation of a type.
142 type TypeRepr struct {
143         Repr       string
144         FormatArgs []interface{}
145 }
146
147 // A Type collects information about a type in both the C and Go worlds.
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 // this pointer type should be represented as a uintptr (deprecated)
156         NotInHeap  bool // this type should have a go:notinheap annotation
157 }
158
159 // A FuncType collects information about a function type in both the C and Go worlds.
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         "loong64":  8,
179         "m68k":     4,
180         "mips":     4,
181         "mipsle":   4,
182         "mips64":   8,
183         "mips64le": 8,
184         "nios2":    4,
185         "ppc":      4,
186         "ppc64":    8,
187         "ppc64le":  8,
188         "riscv":    4,
189         "riscv64":  8,
190         "s390":     4,
191         "s390x":    8,
192         "sh":       4,
193         "shbe":     4,
194         "sparc":    4,
195         "sparc64":  8,
196 }
197
198 var intSizeMap = map[string]int64{
199         "386":      4,
200         "alpha":    8,
201         "amd64":    8,
202         "arm":      4,
203         "arm64":    8,
204         "loong64":  8,
205         "m68k":     4,
206         "mips":     4,
207         "mipsle":   4,
208         "mips64":   8,
209         "mips64le": 8,
210         "nios2":    4,
211         "ppc":      4,
212         "ppc64":    8,
213         "ppc64le":  8,
214         "riscv":    4,
215         "riscv64":  8,
216         "s390":     4,
217         "s390x":    8,
218         "sh":       4,
219         "shbe":     4,
220         "sparc":    4,
221         "sparc64":  8,
222 }
223
224 var cPrefix string
225
226 var fset = token.NewFileSet()
227
228 var dynobj = flag.String("dynimport", "", "if non-empty, print dynamic import data for that file")
229 var dynout = flag.String("dynout", "", "write -dynimport output to this file")
230 var dynpackage = flag.String("dynpackage", "main", "set Go package for -dynimport output")
231 var dynlinker = flag.Bool("dynlinker", false, "record dynamic linker information in -dynimport mode")
232
233 // This flag is for bootstrapping a new Go implementation,
234 // to generate Go types that match the data layout and
235 // constant values used in the host's C libraries and system calls.
236 var godefs = flag.Bool("godefs", false, "for bootstrap: write Go definitions for C file to standard output")
237
238 var srcDir = flag.String("srcdir", "", "source directory")
239 var objDir = flag.String("objdir", "", "object directory")
240 var importPath = flag.String("importpath", "", "import path of package being built (for comments in generated files)")
241 var exportHeader = flag.String("exportheader", "", "where to write export header if any exported functions")
242
243 var gccgo = flag.Bool("gccgo", false, "generate files for use with gccgo")
244 var gccgoprefix = flag.String("gccgoprefix", "", "-fgo-prefix option used with gccgo")
245 var gccgopkgpath = flag.String("gccgopkgpath", "", "-fgo-pkgpath option used with gccgo")
246 var gccgoMangler func(string) string
247 var importRuntimeCgo = flag.Bool("import_runtime_cgo", true, "import runtime/cgo in generated code")
248 var importSyscall = flag.Bool("import_syscall", true, "import syscall in generated code")
249 var trimpath = flag.String("trimpath", "", "applies supplied rewrites or trims prefixes to recorded source file paths")
250
251 var goarch, goos, gomips, gomips64 string
252 var gccBaseCmd []string
253
254 func main() {
255         objabi.AddVersionFlag() // -V
256         flag.Usage = usage
257         flag.Parse()
258
259         if *dynobj != "" {
260                 // cgo -dynimport is essentially a separate helper command
261                 // built into the cgo binary. It scans a gcc-produced executable
262                 // and dumps information about the imported symbols and the
263                 // imported libraries. The 'go build' rules for cgo prepare an
264                 // appropriate executable and then use its import information
265                 // instead of needing to make the linkers duplicate all the
266                 // specialized knowledge gcc has about where to look for imported
267                 // symbols and which ones to use.
268                 dynimport(*dynobj)
269                 return
270         }
271
272         if *godefs {
273                 // Generating definitions pulled from header files,
274                 // to be checked into Go repositories.
275                 // Line numbers are just noise.
276                 conf.Mode &^= printer.SourcePos
277         }
278
279         args := flag.Args()
280         if len(args) < 1 {
281                 usage()
282         }
283
284         // Find first arg that looks like a go file and assume everything before
285         // that are options to pass to gcc.
286         var i int
287         for i = len(args); i > 0; i-- {
288                 if !strings.HasSuffix(args[i-1], ".go") {
289                         break
290                 }
291         }
292         if i == len(args) {
293                 usage()
294         }
295
296         // Save original command line arguments for the godefs generated comment. Relative file
297         // paths in os.Args will be rewritten to absolute file paths in the loop below.
298         osArgs := make([]string, len(os.Args))
299         copy(osArgs, os.Args[:])
300         goFiles := args[i:]
301
302         for _, arg := range args[:i] {
303                 if arg == "-fsanitize=thread" {
304                         tsanProlog = yesTsanProlog
305                 }
306                 if arg == "-fsanitize=memory" {
307                         msanProlog = yesMsanProlog
308                 }
309         }
310
311         p := newPackage(args[:i])
312
313         // We need a C compiler to be available. Check this.
314         var err error
315         gccBaseCmd, err = checkGCCBaseCmd()
316         if err != nil {
317                 fatalf("%v", err)
318                 os.Exit(2)
319         }
320
321         // Record CGO_LDFLAGS from the environment for external linking.
322         if ldflags := os.Getenv("CGO_LDFLAGS"); ldflags != "" {
323                 args, err := splitQuoted(ldflags)
324                 if err != nil {
325                         fatalf("bad CGO_LDFLAGS: %q (%s)", ldflags, err)
326                 }
327                 p.addToFlag("LDFLAGS", args)
328         }
329
330         // Need a unique prefix for the global C symbols that
331         // we use to coordinate between gcc and ourselves.
332         // We already put _cgo_ at the beginning, so the main
333         // concern is other cgo wrappers for the same functions.
334         // Use the beginning of the notsha256 of the input to disambiguate.
335         h := notsha256.New()
336         io.WriteString(h, *importPath)
337         fs := make([]*File, len(goFiles))
338         for i, input := range goFiles {
339                 if *srcDir != "" {
340                         input = filepath.Join(*srcDir, input)
341                 }
342
343                 // Create absolute path for file, so that it will be used in error
344                 // messages and recorded in debug line number information.
345                 // This matches the rest of the toolchain. See golang.org/issue/5122.
346                 if aname, err := filepath.Abs(input); err == nil {
347                         input = aname
348                 }
349
350                 b, err := ioutil.ReadFile(input)
351                 if err != nil {
352                         fatalf("%s", err)
353                 }
354                 if _, err = h.Write(b); err != nil {
355                         fatalf("%s", err)
356                 }
357
358                 // Apply trimpath to the file path. The path won't be read from after this point.
359                 input, _ = objabi.ApplyRewrites(input, *trimpath)
360                 goFiles[i] = input
361
362                 f := new(File)
363                 f.Edit = edit.NewBuffer(b)
364                 f.ParseGo(input, b)
365                 f.DiscardCgoDirectives()
366                 fs[i] = f
367         }
368
369         cPrefix = fmt.Sprintf("_%x", h.Sum(nil)[0:6])
370
371         if *objDir == "" {
372                 // make sure that _obj directory exists, so that we can write
373                 // all the output files there.
374                 os.Mkdir("_obj", 0777)
375                 *objDir = "_obj"
376         }
377         *objDir += string(filepath.Separator)
378
379         for i, input := range goFiles {
380                 f := fs[i]
381                 p.Translate(f)
382                 for _, cref := range f.Ref {
383                         switch cref.Context {
384                         case ctxCall, ctxCall2:
385                                 if cref.Name.Kind != "type" {
386                                         break
387                                 }
388                                 old := *cref.Expr
389                                 *cref.Expr = cref.Name.Type.Go
390                                 f.Edit.Replace(f.offset(old.Pos()), f.offset(old.End()), gofmt(cref.Name.Type.Go))
391                         }
392                 }
393                 if nerrors > 0 {
394                         os.Exit(2)
395                 }
396                 p.PackagePath = f.Package
397                 p.Record(f)
398                 if *godefs {
399                         os.Stdout.WriteString(p.godefs(f, osArgs))
400                 } else {
401                         p.writeOutput(f, input)
402                 }
403         }
404
405         if !*godefs {
406                 p.writeDefs()
407         }
408         if nerrors > 0 {
409                 os.Exit(2)
410         }
411 }
412
413 // newPackage returns a new Package that will invoke
414 // gcc with the additional arguments specified in args.
415 func newPackage(args []string) *Package {
416         goarch = runtime.GOARCH
417         if s := os.Getenv("GOARCH"); s != "" {
418                 goarch = s
419         }
420         goos = runtime.GOOS
421         if s := os.Getenv("GOOS"); s != "" {
422                 goos = s
423         }
424         buildcfg.Check()
425         gomips = buildcfg.GOMIPS
426         gomips64 = buildcfg.GOMIPS64
427         ptrSize := ptrSizeMap[goarch]
428         if ptrSize == 0 {
429                 fatalf("unknown ptrSize for $GOARCH %q", goarch)
430         }
431         intSize := intSizeMap[goarch]
432         if intSize == 0 {
433                 fatalf("unknown intSize for $GOARCH %q", goarch)
434         }
435
436         // Reset locale variables so gcc emits English errors [sic].
437         os.Setenv("LANG", "en_US.UTF-8")
438         os.Setenv("LC_ALL", "C")
439
440         p := &Package{
441                 PtrSize:  ptrSize,
442                 IntSize:  intSize,
443                 CgoFlags: make(map[string][]string),
444                 Written:  make(map[string]bool),
445         }
446         p.addToFlag("CFLAGS", args)
447         return p
448 }
449
450 // Record what needs to be recorded about f.
451 func (p *Package) Record(f *File) {
452         if p.PackageName == "" {
453                 p.PackageName = f.Package
454         } else if p.PackageName != f.Package {
455                 error_(token.NoPos, "inconsistent package names: %s, %s", p.PackageName, f.Package)
456         }
457
458         if p.Name == nil {
459                 p.Name = f.Name
460         } else {
461                 for k, v := range f.Name {
462                         if p.Name[k] == nil {
463                                 p.Name[k] = v
464                         } else if p.incompleteTypedef(p.Name[k].Type) {
465                                 p.Name[k] = v
466                         } else if p.incompleteTypedef(v.Type) {
467                                 // Nothing to do.
468                         } else if _, ok := nameToC[k]; ok {
469                                 // Names we predefine may appear inconsistent
470                                 // if some files typedef them and some don't.
471                                 // Issue 26743.
472                         } else if !reflect.DeepEqual(p.Name[k], v) {
473                                 error_(token.NoPos, "inconsistent definitions for C.%s", fixGo(k))
474                         }
475                 }
476         }
477
478         if f.ExpFunc != nil {
479                 p.ExpFunc = append(p.ExpFunc, f.ExpFunc...)
480                 p.Preamble += "\n" + f.Preamble
481         }
482         p.Decl = append(p.Decl, f.AST.Decls...)
483 }
484
485 // incompleteTypedef reports whether t appears to be an incomplete
486 // typedef definition.
487 func (p *Package) incompleteTypedef(t *Type) bool {
488         return t == nil || (t.Size == 0 && t.Align == -1)
489 }