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/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 }
    403