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