github.com/ltltlt/go-source-code@v0.0.0-20190830023027-95be009773aa/cmd/cgo/main.go (about)

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