...

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

View as plain text