diff options
Diffstat (limited to 'src/cmd/cgo')
-rw-r--r-- | src/cmd/cgo/Makefile | 15 | ||||
-rw-r--r-- | src/cmd/cgo/ast.go | 414 | ||||
-rw-r--r-- | src/cmd/cgo/doc.go | 96 | ||||
-rw-r--r-- | src/cmd/cgo/gcc.go | 1375 | ||||
-rw-r--r-- | src/cmd/cgo/main.go | 268 | ||||
-rw-r--r-- | src/cmd/cgo/out.go | 745 | ||||
-rw-r--r-- | src/cmd/cgo/util.go | 110 |
7 files changed, 3023 insertions, 0 deletions
diff --git a/src/cmd/cgo/Makefile b/src/cmd/cgo/Makefile new file mode 100644 index 000000000..5458c3e4f --- /dev/null +++ b/src/cmd/cgo/Makefile @@ -0,0 +1,15 @@ +# Copyright 2009 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include ../../Make.inc + +TARG=cgo +GOFILES=\ + ast.go\ + gcc.go\ + main.go\ + out.go\ + util.go\ + +include ../../Make.cmd diff --git a/src/cmd/cgo/ast.go b/src/cmd/cgo/ast.go new file mode 100644 index 000000000..73b7313d6 --- /dev/null +++ b/src/cmd/cgo/ast.go @@ -0,0 +1,414 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Parse input AST and prepare Prog structure. + +package main + +import ( + "fmt" + "go/ast" + "go/doc" + "go/parser" + "go/scanner" + "go/token" + "os" + "strings" +) + +func parse(name string, flags uint) *ast.File { + ast1, err := parser.ParseFile(fset, name, nil, flags) + if err != nil { + if list, ok := err.(scanner.ErrorList); ok { + // If err is a scanner.ErrorList, its String will print just + // the first error and then (+n more errors). + // Instead, turn it into a new Error that will return + // details for all the errors. + for _, e := range list { + fmt.Fprintln(os.Stderr, e) + } + os.Exit(2) + } + fatalf("parsing %s: %s", name, err) + } + return ast1 +} + +func sourceLine(n ast.Node) int { + return fset.Position(n.Pos()).Line +} + +// ReadGo populates f with information learned from reading the +// Go source file with the given file name. It gathers the C preamble +// attached to the import "C" comment, a list of references to C.xxx, +// a list of exported functions, and the actual AST, to be rewritten and +// printed. +func (f *File) ReadGo(name string) { + // Two different parses: once with comments, once without. + // The printer is not good enough at printing comments in the + // right place when we start editing the AST behind its back, + // so we use ast1 to look for the doc comments on import "C" + // and on exported functions, and we use ast2 for translating + // and reprinting. + ast1 := parse(name, parser.ParseComments) + ast2 := parse(name, 0) + + f.Package = ast1.Name.Name + f.Name = make(map[string]*Name) + + // In ast1, find the import "C" line and get any extra C preamble. + sawC := false + for _, decl := range ast1.Decls { + d, ok := decl.(*ast.GenDecl) + if !ok { + continue + } + for _, spec := range d.Specs { + s, ok := spec.(*ast.ImportSpec) + if !ok || string(s.Path.Value) != `"C"` { + continue + } + sawC = true + if s.Name != nil { + error(s.Path.Pos(), `cannot rename import "C"`) + } + cg := s.Doc + if cg == nil && len(d.Specs) == 1 { + cg = d.Doc + } + if cg != nil { + f.Preamble += fmt.Sprintf("#line %d %q\n", sourceLine(cg), name) + f.Preamble += doc.CommentText(cg) + "\n" + } + } + } + if !sawC { + error(token.NoPos, `cannot find import "C"`) + } + + // In ast2, strip the import "C" line. + w := 0 + for _, decl := range ast2.Decls { + d, ok := decl.(*ast.GenDecl) + if !ok { + ast2.Decls[w] = decl + w++ + continue + } + ws := 0 + for _, spec := range d.Specs { + s, ok := spec.(*ast.ImportSpec) + if !ok || string(s.Path.Value) != `"C"` { + d.Specs[ws] = spec + ws++ + } + } + if ws == 0 { + continue + } + d.Specs = d.Specs[0:ws] + ast2.Decls[w] = d + w++ + } + ast2.Decls = ast2.Decls[0:w] + + // Accumulate pointers to uses of C.x. + if f.Ref == nil { + f.Ref = make([]*Ref, 0, 8) + } + f.walk(ast2, "prog", (*File).saveRef) + + // Accumulate exported functions. + // The comments are only on ast1 but we need to + // save the function bodies from ast2. + // The first walk fills in ExpFunc, and the + // second walk changes the entries to + // refer to ast2 instead. + f.walk(ast1, "prog", (*File).saveExport) + f.walk(ast2, "prog", (*File).saveExport2) + + f.AST = ast2 +} + +// Save references to C.xxx for later processing. +func (f *File) saveRef(x interface{}, context string) { + n, ok := x.(*ast.Expr) + if !ok { + return + } + if sel, ok := (*n).(*ast.SelectorExpr); ok { + // For now, assume that the only instance of capital C is + // when used as the imported package identifier. + // The parser should take care of scoping in the future, + // so that we will be able to distinguish a "top-level C" + // from a local C. + if l, ok := sel.X.(*ast.Ident); ok && l.Name == "C" { + if context == "as2" { + context = "expr" + } + goname := sel.Sel.Name + if goname == "errno" { + error(sel.Pos(), "cannot refer to errno directly; see documentation") + return + } + name := f.Name[goname] + if name == nil { + name = &Name{ + Go: goname, + } + f.Name[goname] = name + } + f.Ref = append(f.Ref, &Ref{ + Name: name, + Expr: n, + Context: context, + }) + return + } + } +} + +// If a function should be exported add it to ExpFunc. +func (f *File) saveExport(x interface{}, context string) { + n, ok := x.(*ast.FuncDecl) + if !ok { + return + } + + if n.Doc == nil { + return + } + for _, c := range n.Doc.List { + if !strings.HasPrefix(string(c.Text), "//export ") { + continue + } + + name := strings.TrimSpace(string(c.Text[9:])) + if name == "" { + error(c.Pos(), "export missing name") + } + + if name != n.Name.Name { + error(c.Pos(), "export comment has wrong name %q, want %q", name, n.Name.Name) + } + + f.ExpFunc = append(f.ExpFunc, &ExpFunc{ + Func: n, + ExpName: name, + }) + break + } +} + +// Make f.ExpFunc[i] point at the Func from this AST instead of the other one. +func (f *File) saveExport2(x interface{}, context string) { + n, ok := x.(*ast.FuncDecl) + if !ok { + return + } + + for _, exp := range f.ExpFunc { + if exp.Func.Name.Name == n.Name.Name { + exp.Func = n + break + } + } +} + +// walk walks the AST x, calling visit(f, x, context) for each node. +func (f *File) walk(x interface{}, context string, visit func(*File, interface{}, string)) { + visit(f, x, context) + switch n := x.(type) { + case *ast.Expr: + f.walk(*n, context, visit) + + // everything else just recurs + default: + error(token.NoPos, "unexpected type %T in walk", x, visit) + panic("unexpected type") + + case nil: + + // These are ordered and grouped to match ../../pkg/go/ast/ast.go + case *ast.Field: + f.walk(&n.Type, "type", visit) + case *ast.FieldList: + for _, field := range n.List { + f.walk(field, context, visit) + } + case *ast.BadExpr: + case *ast.Ident: + case *ast.Ellipsis: + case *ast.BasicLit: + case *ast.FuncLit: + f.walk(n.Type, "type", visit) + f.walk(n.Body, "stmt", visit) + case *ast.CompositeLit: + f.walk(&n.Type, "type", visit) + f.walk(n.Elts, "expr", visit) + case *ast.ParenExpr: + f.walk(&n.X, context, visit) + case *ast.SelectorExpr: + f.walk(&n.X, "selector", visit) + case *ast.IndexExpr: + f.walk(&n.X, "expr", visit) + f.walk(&n.Index, "expr", visit) + case *ast.SliceExpr: + f.walk(&n.X, "expr", visit) + if n.Low != nil { + f.walk(&n.Low, "expr", visit) + } + if n.High != nil { + f.walk(&n.High, "expr", visit) + } + case *ast.TypeAssertExpr: + f.walk(&n.X, "expr", visit) + f.walk(&n.Type, "type", visit) + case *ast.CallExpr: + if context == "as2" { + f.walk(&n.Fun, "call2", visit) + } else { + f.walk(&n.Fun, "call", visit) + } + f.walk(n.Args, "expr", visit) + case *ast.StarExpr: + f.walk(&n.X, context, visit) + case *ast.UnaryExpr: + f.walk(&n.X, "expr", visit) + case *ast.BinaryExpr: + f.walk(&n.X, "expr", visit) + f.walk(&n.Y, "expr", visit) + case *ast.KeyValueExpr: + f.walk(&n.Key, "expr", visit) + f.walk(&n.Value, "expr", visit) + + case *ast.ArrayType: + f.walk(&n.Len, "expr", visit) + f.walk(&n.Elt, "type", visit) + case *ast.StructType: + f.walk(n.Fields, "field", visit) + case *ast.FuncType: + f.walk(n.Params, "field", visit) + if n.Results != nil { + f.walk(n.Results, "field", visit) + } + case *ast.InterfaceType: + f.walk(n.Methods, "field", visit) + case *ast.MapType: + f.walk(&n.Key, "type", visit) + f.walk(&n.Value, "type", visit) + case *ast.ChanType: + f.walk(&n.Value, "type", visit) + + case *ast.BadStmt: + case *ast.DeclStmt: + f.walk(n.Decl, "decl", visit) + case *ast.EmptyStmt: + case *ast.LabeledStmt: + f.walk(n.Stmt, "stmt", visit) + case *ast.ExprStmt: + f.walk(&n.X, "expr", visit) + case *ast.SendStmt: + f.walk(&n.Chan, "expr", visit) + f.walk(&n.Value, "expr", visit) + case *ast.IncDecStmt: + f.walk(&n.X, "expr", visit) + case *ast.AssignStmt: + f.walk(n.Lhs, "expr", visit) + if len(n.Lhs) == 2 && len(n.Rhs) == 1 { + f.walk(n.Rhs, "as2", visit) + } else { + f.walk(n.Rhs, "expr", visit) + } + case *ast.GoStmt: + f.walk(n.Call, "expr", visit) + case *ast.DeferStmt: + f.walk(n.Call, "expr", visit) + case *ast.ReturnStmt: + f.walk(n.Results, "expr", visit) + case *ast.BranchStmt: + case *ast.BlockStmt: + f.walk(n.List, context, visit) + case *ast.IfStmt: + f.walk(n.Init, "stmt", visit) + f.walk(&n.Cond, "expr", visit) + f.walk(n.Body, "stmt", visit) + f.walk(n.Else, "stmt", visit) + case *ast.CaseClause: + if context == "typeswitch" { + context = "type" + } else { + context = "expr" + } + f.walk(n.List, context, visit) + f.walk(n.Body, "stmt", visit) + case *ast.SwitchStmt: + f.walk(n.Init, "stmt", visit) + f.walk(&n.Tag, "expr", visit) + f.walk(n.Body, "switch", visit) + case *ast.TypeSwitchStmt: + f.walk(n.Init, "stmt", visit) + f.walk(n.Assign, "stmt", visit) + f.walk(n.Body, "typeswitch", visit) + case *ast.CommClause: + f.walk(n.Comm, "stmt", visit) + f.walk(n.Body, "stmt", visit) + case *ast.SelectStmt: + f.walk(n.Body, "stmt", visit) + case *ast.ForStmt: + f.walk(n.Init, "stmt", visit) + f.walk(&n.Cond, "expr", visit) + f.walk(n.Post, "stmt", visit) + f.walk(n.Body, "stmt", visit) + case *ast.RangeStmt: + f.walk(&n.Key, "expr", visit) + f.walk(&n.Value, "expr", visit) + f.walk(&n.X, "expr", visit) + f.walk(n.Body, "stmt", visit) + + case *ast.ImportSpec: + case *ast.ValueSpec: + f.walk(&n.Type, "type", visit) + f.walk(n.Values, "expr", visit) + case *ast.TypeSpec: + f.walk(&n.Type, "type", visit) + + case *ast.BadDecl: + case *ast.GenDecl: + f.walk(n.Specs, "spec", visit) + case *ast.FuncDecl: + if n.Recv != nil { + f.walk(n.Recv, "field", visit) + } + f.walk(n.Type, "type", visit) + if n.Body != nil { + f.walk(n.Body, "stmt", visit) + } + + case *ast.File: + f.walk(n.Decls, "decl", visit) + + case *ast.Package: + for _, file := range n.Files { + f.walk(file, "file", visit) + } + + case []ast.Decl: + for _, d := range n { + f.walk(d, context, visit) + } + case []ast.Expr: + for i := range n { + f.walk(&n[i], context, visit) + } + case []ast.Stmt: + for _, s := range n { + f.walk(s, context, visit) + } + case []ast.Spec: + for _, s := range n { + f.walk(s, context, visit) + } + } +} diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go new file mode 100644 index 000000000..63413825a --- /dev/null +++ b/src/cmd/cgo/doc.go @@ -0,0 +1,96 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* + +Cgo enables the creation of Go packages that call C code. + +Usage: cgo [compiler options] file.go + +The compiler options are passed through uninterpreted when +invoking gcc to compile the C parts of the package. + +The input file.go is a syntactically valid Go source file that imports +the pseudo-package "C" and then refers to types such as C.size_t, +variables such as C.stdout, or functions such as C.putchar. + +If the import of "C" is immediately preceded by a comment, that +comment is used as a header when compiling the C parts of +the package. For example: + + // #include <stdio.h> + // #include <errno.h> + import "C" + +CFLAGS and LDFLAGS may be defined with pseudo #cgo directives +within these comments to tweak the behavior of gcc. Values defined +in multiple directives are concatenated together. Options prefixed +by $GOOS, $GOARCH, or $GOOS/$GOARCH are only defined in matching +systems. For example: + + // #cgo CFLAGS: -DPNG_DEBUG=1 + // #cgo linux CFLAGS: -DLINUX=1 + // #cgo LDFLAGS: -lpng + // #include <png.h> + import "C" + +Alternatively, CFLAGS and LDFLAGS may be obtained via the pkg-config +tool using a '#cgo pkg-config:' directive followed by the package names. +For example: + + // #cgo pkg-config: png cairo + // #include <png.h> + import "C" + +Within the Go file, C identifiers or field names that are keywords in Go +can be accessed by prefixing them with an underscore: if x points at a C +struct with a field named "type", x._type accesses the field. + +The standard C numeric types are available under the names +C.char, C.schar (signed char), C.uchar (unsigned char), +C.short, C.ushort (unsigned short), C.int, C.uint (unsigned int), +C.long, C.ulong (unsigned long), C.longlong (long long), +C.ulonglong (unsigned long long), C.float, C.double. +The C type void* is represented by Go's unsafe.Pointer. + +To access a struct, union, or enum type directly, prefix it with +struct_, union_, or enum_, as in C.struct_stat. + +Any C function that returns a value may be called in a multiple +assignment context to retrieve both the return value and the +C errno variable as an os.Error. For example: + + n, err := C.atoi("abc") + +In C, a function argument written as a fixed size array +actually requires a pointer to the first element of the array. +C compilers are aware of this calling convention and adjust +the call accordingly, but Go cannot. In Go, you must pass +the pointer to the first element explicitly: C.f(&x[0]). + +A few special functions convert between Go and C types +by making copies of the data. In pseudo-Go definitions: + + // Go string to C string + func C.CString(string) *C.char + + // C string to Go string + func C.GoString(*C.char) string + + // C string, length to Go string + func C.GoStringN(*C.char, C.int) string + + // C pointer, length to Go []byte + func C.GoBytes(unsafe.Pointer, C.int) []byte + +Cgo transforms the input file into four output files: two Go source +files, a C file for 6c (or 8c or 5c), and a C file for gcc. + +The standard package makefile rules in Make.pkg automate the +process of using cgo. See $GOROOT/misc/cgo/stdio and +$GOROOT/misc/cgo/gmp for examples. + +Cgo does not yet work with gccgo. +*/ +package documentation diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go new file mode 100644 index 000000000..7ec4d8ccf --- /dev/null +++ b/src/cmd/cgo/gcc.go @@ -0,0 +1,1375 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Annotate Ref in Prog with C types by parsing gcc debug output. +// Conversion of debug output to Go types. + +package main + +import ( + "bytes" + "debug/dwarf" + "debug/elf" + "debug/macho" + "debug/pe" + "encoding/binary" + "flag" + "fmt" + "go/ast" + "go/parser" + "go/token" + "os" + "runtime" + "strconv" + "strings" + "unicode" +) + +var debugDefine = flag.Bool("debug-define", false, "print relevant #defines") +var debugGcc = flag.Bool("debug-gcc", false, "print gcc invocations") + +var nameToC = map[string]string{ + "schar": "signed char", + "uchar": "unsigned char", + "ushort": "unsigned short", + "uint": "unsigned int", + "ulong": "unsigned long", + "longlong": "long long", + "ulonglong": "unsigned long long", + "complexfloat": "float complex", + "complexdouble": "double complex", +} + +// cname returns the C name to use for C.s. +// The expansions are listed in nameToC and also +// struct_foo becomes "struct foo", and similarly for +// union and enum. +func cname(s string) string { + if t, ok := nameToC[s]; ok { + return t + } + + if strings.HasPrefix(s, "struct_") { + return "struct " + s[len("struct_"):] + } + if strings.HasPrefix(s, "union_") { + return "union " + s[len("union_"):] + } + if strings.HasPrefix(s, "enum_") { + return "enum " + s[len("enum_"):] + } + return s +} + +// ParseFlags extracts #cgo CFLAGS and LDFLAGS options from the file +// preamble. Multiple occurrences are concatenated with a separating space, +// even across files. +func (p *Package) ParseFlags(f *File, srcfile string) { + linesIn := strings.Split(f.Preamble, "\n") + linesOut := make([]string, 0, len(linesIn)) + +NextLine: + for _, line := range linesIn { + l := strings.TrimSpace(line) + if len(l) < 5 || l[:4] != "#cgo" || !unicode.IsSpace(int(l[4])) { + linesOut = append(linesOut, line) + continue + } + + l = strings.TrimSpace(l[4:]) + fields := strings.SplitN(l, ":", 2) + if len(fields) != 2 { + fatalf("%s: bad #cgo line: %s", srcfile, line) + } + + var k string + kf := strings.Fields(fields[0]) + switch len(kf) { + case 1: + k = kf[0] + case 2: + k = kf[1] + switch kf[0] { + case runtime.GOOS: + case runtime.GOARCH: + case runtime.GOOS + "/" + runtime.GOARCH: + default: + continue NextLine + } + default: + fatalf("%s: bad #cgo option: %s", srcfile, fields[0]) + } + + args, err := splitQuoted(fields[1]) + if err != nil { + fatalf("%s: bad #cgo option %s: %s", srcfile, k, err) + } + for _, arg := range args { + if !safeName(arg) { + fatalf("%s: #cgo option %s is unsafe: %s", srcfile, k, arg) + } + } + + switch k { + + case "CFLAGS", "LDFLAGS": + p.addToFlag(k, args) + + case "pkg-config": + cflags, ldflags, err := pkgConfig(args) + if err != nil { + fatalf("%s: bad #cgo option %s: %s", srcfile, k, err) + } + p.addToFlag("CFLAGS", cflags) + p.addToFlag("LDFLAGS", ldflags) + + default: + fatalf("%s: unsupported #cgo option %s", srcfile, k) + + } + } + f.Preamble = strings.Join(linesOut, "\n") +} + +// addToFlag appends args to flag. All flags are later written out onto the +// _cgo_flags file for the build system to use. +func (p *Package) addToFlag(flag string, args []string) { + if oldv, ok := p.CgoFlags[flag]; ok { + p.CgoFlags[flag] = oldv + " " + strings.Join(args, " ") + } else { + p.CgoFlags[flag] = strings.Join(args, " ") + } + if flag == "CFLAGS" { + // We'll also need these when preprocessing for dwarf information. + p.GccOptions = append(p.GccOptions, args...) + } +} + +// pkgConfig runs pkg-config and extracts --libs and --cflags information +// for packages. +func pkgConfig(packages []string) (cflags, ldflags []string, err os.Error) { + for _, name := range packages { + if len(name) == 0 || name[0] == '-' { + return nil, nil, os.NewError(fmt.Sprintf("invalid name: %q", name)) + } + } + + args := append([]string{"pkg-config", "--cflags"}, packages...) + stdout, stderr, ok := run(nil, args) + if !ok { + os.Stderr.Write(stderr) + return nil, nil, os.NewError("pkg-config failed") + } + cflags, err = splitQuoted(string(stdout)) + if err != nil { + return + } + + args = append([]string{"pkg-config", "--libs"}, packages...) + stdout, stderr, ok = run(nil, args) + if !ok { + os.Stderr.Write(stderr) + return nil, nil, os.NewError("pkg-config failed") + } + ldflags, err = splitQuoted(string(stdout)) + return +} + +// splitQuoted splits the string s around each instance of one or more consecutive +// white space characters while taking into account quotes and escaping, and +// returns an array of substrings of s or an empty list if s contains only white space. +// Single quotes and double quotes are recognized to prevent splitting within the +// quoted region, and are removed from the resulting substrings. If a quote in s +// isn't closed err will be set and r will have the unclosed argument as the +// last element. The backslash is used for escaping. +// +// For example, the following string: +// +// `a b:"c d" 'e''f' "g\""` +// +// Would be parsed as: +// +// []string{"a", "b:c d", "ef", `g"`} +// +func splitQuoted(s string) (r []string, err os.Error) { + var args []string + arg := make([]int, len(s)) + escaped := false + quoted := false + quote := 0 + i := 0 + for _, rune := range s { + switch { + case escaped: + escaped = false + case rune == '\\': + escaped = true + continue + case quote != 0: + if rune == quote { + quote = 0 + continue + } + case rune == '"' || rune == '\'': + quoted = true + quote = rune + continue + case unicode.IsSpace(rune): + if quoted || i > 0 { + quoted = false + args = append(args, string(arg[:i])) + i = 0 + } + continue + } + arg[i] = rune + i++ + } + if quoted || i > 0 { + args = append(args, string(arg[:i])) + } + if quote != 0 { + err = os.NewError("unclosed quote") + } else if escaped { + err = os.NewError("unfinished escaping") + } + return args, err +} + +var safeBytes = []byte("+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz") + +func safeName(s string) bool { + if s == "" { + return false + } + for i := 0; i < len(s); i++ { + if c := s[i]; c < 0x80 && bytes.IndexByte(safeBytes, c) < 0 { + return false + } + } + return true +} + +// Translate rewrites f.AST, the original Go input, to remove +// references to the imported package C, replacing them with +// references to the equivalent Go types, functions, and variables. +func (p *Package) Translate(f *File) { + for _, cref := range f.Ref { + // Convert C.ulong to C.unsigned long, etc. + cref.Name.C = cname(cref.Name.Go) + } + p.loadDefines(f) + needType := p.guessKinds(f) + if len(needType) > 0 { + p.loadDWARF(f, needType) + } + p.rewriteRef(f) +} + +// loadDefines coerces gcc into spitting out the #defines in use +// in the file f and saves relevant renamings in f.Name[name].Define. +func (p *Package) loadDefines(f *File) { + var b bytes.Buffer + b.WriteString(builtinProlog) + b.WriteString(f.Preamble) + stdout := p.gccDefines(b.Bytes()) + + for _, line := range strings.Split(stdout, "\n") { + if len(line) < 9 || line[0:7] != "#define" { + continue + } + + line = strings.TrimSpace(line[8:]) + + var key, val string + spaceIndex := strings.Index(line, " ") + tabIndex := strings.Index(line, "\t") + + if spaceIndex == -1 && tabIndex == -1 { + continue + } else if tabIndex == -1 || (spaceIndex != -1 && spaceIndex < tabIndex) { + key = line[0:spaceIndex] + val = strings.TrimSpace(line[spaceIndex:]) + } else { + key = line[0:tabIndex] + val = strings.TrimSpace(line[tabIndex:]) + } + + if n := f.Name[key]; n != nil { + if *debugDefine { + fmt.Fprintf(os.Stderr, "#define %s %s\n", key, val) + } + n.Define = val + } + } +} + +// guessKinds tricks gcc into revealing the kind of each +// name xxx for the references C.xxx in the Go input. +// The kind is either a constant, type, or variable. +func (p *Package) guessKinds(f *File) []*Name { + // Coerce gcc into telling us whether each name is + // a type, a value, or undeclared. We compile a function + // containing the line: + // name; + // If name is a type, gcc will print: + // cgo-test:2: warning: useless type name in empty declaration + // If name is a value, gcc will print + // cgo-test:2: warning: statement with no effect + // If name is undeclared, gcc will print + // cgo-test:2: error: 'name' undeclared (first use in this function) + // A line number directive causes the line number to + // correspond to the index in the names array. + // + // The line also has an enum declaration: + // name; enum { _cgo_enum_1 = name }; + // If name is not a constant, gcc will print: + // cgo-test:4: error: enumerator value for '_cgo_enum_4' is not an integer constant + // we assume lines without that error are constants. + + // Make list of names that need sniffing, type lookup. + toSniff := make([]*Name, 0, len(f.Name)) + needType := make([]*Name, 0, len(f.Name)) + + for _, n := range f.Name { + // If we've already found this name as a #define + // and we can translate it as a constant value, do so. + if n.Define != "" { + ok := false + if _, err := strconv.Atoi(n.Define); err == nil { + ok = true + } else if n.Define[0] == '"' || n.Define[0] == '\'' { + _, err := parser.ParseExpr(fset, "", n.Define) + if err == nil { + ok = true + } + } + if ok { + n.Kind = "const" + n.Const = n.Define + continue + } + + if isName(n.Define) { + n.C = n.Define + } + } + + // If this is a struct, union, or enum type name, + // record the kind but also that we need type information. + if strings.HasPrefix(n.C, "struct ") || strings.HasPrefix(n.C, "union ") || strings.HasPrefix(n.C, "enum ") { + n.Kind = "type" + i := len(needType) + needType = needType[0 : i+1] + needType[i] = n + continue + } + + i := len(toSniff) + toSniff = toSniff[0 : i+1] + toSniff[i] = n + } + + if len(toSniff) == 0 { + return needType + } + + var b bytes.Buffer + b.WriteString(builtinProlog) + b.WriteString(f.Preamble) + b.WriteString("void __cgo__f__(void) {\n") + b.WriteString("#line 0 \"cgo-test\"\n") + for i, n := range toSniff { + fmt.Fprintf(&b, "%s; enum { _cgo_enum_%d = %s }; /* cgo-test:%d */\n", n.C, i, n.C, i) + } + b.WriteString("}\n") + stderr := p.gccErrors(b.Bytes()) + if stderr == "" { + fatalf("gcc produced no output\non input:\n%s", b.Bytes()) + } + + names := make([]*Name, len(toSniff)) + copy(names, toSniff) + + isConst := make([]bool, len(toSniff)) + for i := range isConst { + isConst[i] = true // until proven otherwise + } + + for _, line := range strings.Split(stderr, "\n") { + if len(line) < 9 || line[0:9] != "cgo-test:" { + // the user will see any compiler errors when the code is compiled later. + continue + } + line = line[9:] + colon := strings.Index(line, ":") + if colon < 0 { + continue + } + i, err := strconv.Atoi(line[0:colon]) + if err != nil { + continue + } + what := "" + switch { + default: + continue + case strings.Contains(line, ": useless type name in empty declaration"): + what = "type" + isConst[i] = false + case strings.Contains(line, ": statement with no effect"): + what = "not-type" // const or func or var + case strings.Contains(line, "undeclared"): + error(token.NoPos, "%s", strings.TrimSpace(line[colon+1:])) + case strings.Contains(line, "is not an integer constant"): + isConst[i] = false + continue + } + n := toSniff[i] + if n == nil { + continue + } + toSniff[i] = nil + n.Kind = what + + j := len(needType) + needType = needType[0 : j+1] + needType[j] = n + } + for i, b := range isConst { + if b { + names[i].Kind = "const" + } + } + for _, n := range toSniff { + if n == nil { + continue + } + if n.Kind != "" { + continue + } + error(token.NoPos, "could not determine kind of name for C.%s", n.Go) + } + if nerrors > 0 { + fatalf("unresolved names") + } + return needType +} + +// loadDWARF parses the DWARF debug information generated +// by gcc to learn the details of the constants, variables, and types +// being referred to as C.xxx. +func (p *Package) loadDWARF(f *File, names []*Name) { + // Extract the types from the DWARF section of an object + // from a well-formed C program. Gcc only generates DWARF info + // for symbols in the object file, so it is not enough to print the + // preamble and hope the symbols we care about will be there. + // Instead, emit + // typeof(names[i]) *__cgo__i; + // for each entry in names and then dereference the type we + // learn for __cgo__i. + var b bytes.Buffer + b.WriteString(builtinProlog) + b.WriteString(f.Preamble) + for i, n := range names { + fmt.Fprintf(&b, "typeof(%s) *__cgo__%d;\n", n.C, i) + if n.Kind == "const" { + fmt.Fprintf(&b, "enum { __cgo_enum__%d = %s };\n", i, n.C) + } + } + + // Apple's LLVM-based gcc does not include the enumeration + // names and values in its DWARF debug output. In case we're + // using such a gcc, create a data block initialized with the values. + // We can read them out of the object file. + fmt.Fprintf(&b, "long long __cgodebug_data[] = {\n") + for _, n := range names { + if n.Kind == "const" { + fmt.Fprintf(&b, "\t%s,\n", n.C) + } else { + fmt.Fprintf(&b, "\t0,\n") + } + } + fmt.Fprintf(&b, "\t0\n") + fmt.Fprintf(&b, "};\n") + + d, bo, debugData := p.gccDebug(b.Bytes()) + enumVal := make([]int64, len(debugData)/8) + for i := range enumVal { + enumVal[i] = int64(bo.Uint64(debugData[i*8:])) + } + + // Scan DWARF info for top-level TagVariable entries with AttrName __cgo__i. + types := make([]dwarf.Type, len(names)) + enums := make([]dwarf.Offset, len(names)) + nameToIndex := make(map[*Name]int) + for i, n := range names { + nameToIndex[n] = i + } + r := d.Reader() + for { + e, err := r.Next() + if err != nil { + fatalf("reading DWARF entry: %s", err) + } + if e == nil { + break + } + switch e.Tag { + case dwarf.TagEnumerationType: + offset := e.Offset + for { + e, err := r.Next() + if err != nil { + fatalf("reading DWARF entry: %s", err) + } + if e.Tag == 0 { + break + } + if e.Tag == dwarf.TagEnumerator { + entryName := e.Val(dwarf.AttrName).(string) + if strings.HasPrefix(entryName, "__cgo_enum__") { + n, _ := strconv.Atoi(entryName[len("__cgo_enum__"):]) + if 0 <= n && n < len(names) { + enums[n] = offset + } + } + } + } + case dwarf.TagVariable: + name, _ := e.Val(dwarf.AttrName).(string) + typOff, _ := e.Val(dwarf.AttrType).(dwarf.Offset) + if name == "" || typOff == 0 { + fatalf("malformed DWARF TagVariable entry") + } + if !strings.HasPrefix(name, "__cgo__") { + break + } + typ, err := d.Type(typOff) + if err != nil { + fatalf("loading DWARF type: %s", err) + } + t, ok := typ.(*dwarf.PtrType) + if !ok || t == nil { + fatalf("internal error: %s has non-pointer type", name) + } + i, err := strconv.Atoi(name[7:]) + if err != nil { + fatalf("malformed __cgo__ name: %s", name) + } + if enums[i] != 0 { + t, err := d.Type(enums[i]) + if err != nil { + fatalf("loading DWARF type: %s", err) + } + types[i] = t + } else { + types[i] = t.Type + } + } + if e.Tag != dwarf.TagCompileUnit { + r.SkipChildren() + } + } + + // Record types and typedef information. + var conv typeConv + conv.Init(p.PtrSize) + for i, n := range names { + f, fok := types[i].(*dwarf.FuncType) + if n.Kind != "type" && fok { + n.Kind = "func" + n.FuncType = conv.FuncType(f) + } else { + n.Type = conv.Type(types[i]) + if enums[i] != 0 && n.Type.EnumValues != nil { + k := fmt.Sprintf("__cgo_enum__%d", i) + n.Kind = "const" + n.Const = strconv.Itoa64(n.Type.EnumValues[k]) + // Remove injected enum to ensure the value will deep-compare + // equally in future loads of the same constant. + n.Type.EnumValues[k] = 0, false + } else if n.Kind == "const" && i < len(enumVal) { + n.Const = strconv.Itoa64(enumVal[i]) + } + } + } + +} + +// rewriteRef rewrites all the C.xxx references in f.AST to refer to the +// Go equivalents, now that we have figured out the meaning of all +// the xxx. +func (p *Package) rewriteRef(f *File) { + // Assign mangled names. + for _, n := range f.Name { + if n.Kind == "not-type" { + n.Kind = "var" + } + if n.Mangle == "" { + n.Mangle = "_C" + n.Kind + "_" + n.Go + } + } + + // Now that we have all the name types filled in, + // scan through the Refs to identify the ones that + // are trying to do a ,err call. Also check that + // functions are only used in calls. + for _, r := range f.Ref { + if r.Name.Kind == "const" && r.Name.Const == "" { + error(r.Pos(), "unable to find value of constant C.%s", r.Name.Go) + } + var expr ast.Expr = ast.NewIdent(r.Name.Mangle) // default + switch r.Context { + case "call", "call2": + if r.Name.Kind != "func" { + if r.Name.Kind == "type" { + r.Context = "type" + expr = r.Name.Type.Go + break + } + error(r.Pos(), "call of non-function C.%s", r.Name.Go) + break + } + if r.Context == "call2" { + if r.Name.FuncType.Result == nil { + error(r.Pos(), "assignment count mismatch: 2 = 0") + } + // Invent new Name for the two-result function. + n := f.Name["2"+r.Name.Go] + if n == nil { + n = new(Name) + *n = *r.Name + n.AddError = true + n.Mangle = "_C2func_" + n.Go + f.Name["2"+r.Name.Go] = n + } + expr = ast.NewIdent(n.Mangle) + r.Name = n + break + } + case "expr": + if r.Name.Kind == "func" { + error(r.Pos(), "must call C.%s", r.Name.Go) + } + if r.Name.Kind == "type" { + // Okay - might be new(T) + expr = r.Name.Type.Go + } + if r.Name.Kind == "var" { + expr = &ast.StarExpr{X: expr} + } + + case "type": + if r.Name.Kind != "type" { + error(r.Pos(), "expression C.%s used as type", r.Name.Go) + } else { + expr = r.Name.Type.Go + } + default: + if r.Name.Kind == "func" { + error(r.Pos(), "must call C.%s", r.Name.Go) + } + } + *r.Expr = expr + } +} + +// gccName returns the name of the compiler to run. Use $GCC if set in +// the environment, otherwise just "gcc". + +func (p *Package) gccName() (ret string) { + if ret = os.Getenv("GCC"); ret == "" { + ret = "gcc" + } + return +} + +// gccMachine returns the gcc -m flag to use, either "-m32" or "-m64". +func (p *Package) gccMachine() []string { + switch runtime.GOARCH { + case "amd64": + return []string{"-m64"} + case "386": + return []string{"-m32"} + } + return nil +} + +var gccTmp = objDir + "_cgo_.o" + +// gccCmd returns the gcc command line to use for compiling +// the input. +func (p *Package) gccCmd() []string { + c := []string{ + p.gccName(), + "-Wall", // many warnings + "-Werror", // warnings are errors + "-o" + gccTmp, // write object to tmp + "-gdwarf-2", // generate DWARF v2 debugging symbols + "-fno-eliminate-unused-debug-types", // gets rid of e.g. untyped enum otherwise + "-c", // do not link + "-xc", // input language is C + } + c = append(c, p.GccOptions...) + c = append(c, p.gccMachine()...) + c = append(c, "-") //read input from standard input + return c +} + +// gccDebug runs gcc -gdwarf-2 over the C program stdin and +// returns the corresponding DWARF data and, if present, debug data block. +func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) { + runGcc(stdin, p.gccCmd()) + + if f, err := macho.Open(gccTmp); err == nil { + d, err := f.DWARF() + if err != nil { + fatalf("cannot load DWARF output from %s: %v", gccTmp, err) + } + var data []byte + if f.Symtab != nil { + for i := range f.Symtab.Syms { + s := &f.Symtab.Syms[i] + // Mach-O still uses a leading _ to denote non-assembly symbols. + if s.Name == "_"+"__cgodebug_data" { + // Found it. Now find data section. + if i := int(s.Sect) - 1; 0 <= i && i < len(f.Sections) { + sect := f.Sections[i] + if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size { + if sdat, err := sect.Data(); err == nil { + data = sdat[s.Value-sect.Addr:] + } + } + } + } + } + } + return d, f.ByteOrder, data + } + + // Can skip debug data block in ELF and PE for now. + // The DWARF information is complete. + + if f, err := elf.Open(gccTmp); err == nil { + d, err := f.DWARF() + if err != nil { + fatalf("cannot load DWARF output from %s: %v", gccTmp, err) + } + return d, f.ByteOrder, nil + } + + if f, err := pe.Open(gccTmp); err == nil { + d, err := f.DWARF() + if err != nil { + fatalf("cannot load DWARF output from %s: %v", gccTmp, err) + } + return d, binary.LittleEndian, nil + } + + fatalf("cannot parse gcc output %s as ELF, Mach-O, PE object", gccTmp) + panic("not reached") +} + +// gccDefines runs gcc -E -dM -xc - over the C program stdin +// and returns the corresponding standard output, which is the +// #defines that gcc encountered while processing the input +// and its included files. +func (p *Package) gccDefines(stdin []byte) string { + base := []string{p.gccName(), "-E", "-dM", "-xc"} + base = append(base, p.gccMachine()...) + stdout, _ := runGcc(stdin, append(append(base, p.GccOptions...), "-")) + return stdout +} + +// gccErrors runs gcc over the C program stdin and returns +// the errors that gcc prints. That is, this function expects +// gcc to fail. +func (p *Package) gccErrors(stdin []byte) string { + // TODO(rsc): require failure + args := p.gccCmd() + if *debugGcc { + fmt.Fprintf(os.Stderr, "$ %s <<EOF\n", strings.Join(args, " ")) + os.Stderr.Write(stdin) + fmt.Fprint(os.Stderr, "EOF\n") + } + stdout, stderr, _ := run(stdin, args) + if *debugGcc { + os.Stderr.Write(stdout) + os.Stderr.Write(stderr) + } + return string(stderr) +} + +// runGcc runs the gcc command line args with stdin on standard input. +// If the command exits with a non-zero exit status, runGcc prints +// details about what was run and exits. +// Otherwise runGcc returns the data written to standard output and standard error. +// Note that for some of the uses we expect useful data back +// on standard error, but for those uses gcc must still exit 0. +func runGcc(stdin []byte, args []string) (string, string) { + if *debugGcc { + fmt.Fprintf(os.Stderr, "$ %s <<EOF\n", strings.Join(args, " ")) + os.Stderr.Write(stdin) + fmt.Fprint(os.Stderr, "EOF\n") + } + stdout, stderr, ok := run(stdin, args) + if *debugGcc { + os.Stderr.Write(stdout) + os.Stderr.Write(stderr) + } + if !ok { + os.Stderr.Write(stderr) + os.Exit(2) + } + return string(stdout), string(stderr) +} + +// A typeConv is a translator from dwarf types to Go types +// with equivalent memory layout. +type typeConv struct { + // Cache of already-translated or in-progress types. + m map[dwarf.Type]*Type + typedef map[string]ast.Expr + + // Predeclared types. + bool ast.Expr + byte ast.Expr // denotes padding + int8, int16, int32, int64 ast.Expr + uint8, uint16, uint32, uint64, uintptr ast.Expr + float32, float64 ast.Expr + complex64, complex128 ast.Expr + void ast.Expr + unsafePointer ast.Expr + string ast.Expr + + ptrSize int64 +} + +var tagGen int +var typedef = make(map[string]ast.Expr) + +func (c *typeConv) Init(ptrSize int64) { + c.ptrSize = ptrSize + c.m = make(map[dwarf.Type]*Type) + c.bool = c.Ident("bool") + c.byte = c.Ident("byte") + c.int8 = c.Ident("int8") + c.int16 = c.Ident("int16") + c.int32 = c.Ident("int32") + c.int64 = c.Ident("int64") + c.uint8 = c.Ident("uint8") + c.uint16 = c.Ident("uint16") + c.uint32 = c.Ident("uint32") + c.uint64 = c.Ident("uint64") + c.uintptr = c.Ident("uintptr") + c.float32 = c.Ident("float32") + c.float64 = c.Ident("float64") + c.complex64 = c.Ident("complex64") + c.complex128 = c.Ident("complex128") + c.unsafePointer = c.Ident("unsafe.Pointer") + c.void = c.Ident("void") + c.string = c.Ident("string") +} + +// base strips away qualifiers and typedefs to get the underlying type +func base(dt dwarf.Type) dwarf.Type { + for { + if d, ok := dt.(*dwarf.QualType); ok { + dt = d.Type + continue + } + if d, ok := dt.(*dwarf.TypedefType); ok { + dt = d.Type + continue + } + break + } + return dt +} + +// Map from dwarf text names to aliases we use in package "C". +var dwarfToName = map[string]string{ + "long int": "long", + "long unsigned int": "ulong", + "unsigned int": "uint", + "short unsigned int": "ushort", + "short int": "short", + "long long int": "longlong", + "long long unsigned int": "ulonglong", + "signed char": "schar", + "float complex": "complexfloat", + "double complex": "complexdouble", +} + +const signedDelta = 64 + +// String returns the current type representation. Format arguments +// are assembled within this method so that any changes in mutable +// values are taken into account. +func (tr *TypeRepr) String() string { + if len(tr.Repr) == 0 { + return "" + } + if len(tr.FormatArgs) == 0 { + return tr.Repr + } + return fmt.Sprintf(tr.Repr, tr.FormatArgs...) +} + +// Empty returns true if the result of String would be "". +func (tr *TypeRepr) Empty() bool { + return len(tr.Repr) == 0 +} + +// Set modifies the type representation. +// If fargs are provided, repr is used as a format for fmt.Sprintf. +// Otherwise, repr is used unprocessed as the type representation. +func (tr *TypeRepr) Set(repr string, fargs ...interface{}) { + tr.Repr = repr + tr.FormatArgs = fargs +} + +// Type returns a *Type with the same memory layout as +// dtype when used as the type of a variable or a struct field. +func (c *typeConv) Type(dtype dwarf.Type) *Type { + if t, ok := c.m[dtype]; ok { + if t.Go == nil { + fatalf("type conversion loop at %s", dtype) + } + return t + } + + t := new(Type) + t.Size = dtype.Size() + t.Align = -1 + t.C = &TypeRepr{Repr: dtype.Common().Name} + c.m[dtype] = t + + if t.Size < 0 { + // Unsized types are [0]byte + t.Size = 0 + t.Go = c.Opaque(0) + if t.C.Empty() { + t.C.Set("void") + } + return t + } + + switch dt := dtype.(type) { + default: + fatalf("unexpected type: %s", dtype) + + case *dwarf.AddrType: + if t.Size != c.ptrSize { + fatalf("unexpected: %d-byte address type - %s", t.Size, dtype) + } + t.Go = c.uintptr + t.Align = t.Size + + case *dwarf.ArrayType: + if dt.StrideBitSize > 0 { + // Cannot represent bit-sized elements in Go. + t.Go = c.Opaque(t.Size) + break + } + gt := &ast.ArrayType{ + Len: c.intExpr(dt.Count), + } + t.Go = gt // publish before recursive call + sub := c.Type(dt.Type) + t.Align = sub.Align + gt.Elt = sub.Go + t.C.Set("typeof(%s[%d])", sub.C, dt.Count) + + case *dwarf.BoolType: + t.Go = c.bool + t.Align = c.ptrSize + + case *dwarf.CharType: + if t.Size != 1 { + fatalf("unexpected: %d-byte char type - %s", t.Size, dtype) + } + t.Go = c.int8 + t.Align = 1 + + case *dwarf.EnumType: + if t.Align = t.Size; t.Align >= c.ptrSize { + t.Align = c.ptrSize + } + t.C.Set("enum " + dt.EnumName) + signed := 0 + t.EnumValues = make(map[string]int64) + for _, ev := range dt.Val { + t.EnumValues[ev.Name] = ev.Val + if ev.Val < 0 { + signed = signedDelta + } + } + switch t.Size + int64(signed) { + default: + fatalf("unexpected: %d-byte enum type - %s", t.Size, dtype) + case 1: + t.Go = c.uint8 + case 2: + t.Go = c.uint16 + case 4: + t.Go = c.uint32 + case 8: + t.Go = c.uint64 + case 1 + signedDelta: + t.Go = c.int8 + case 2 + signedDelta: + t.Go = c.int16 + case 4 + signedDelta: + t.Go = c.int32 + case 8 + signedDelta: + t.Go = c.int64 + } + + case *dwarf.FloatType: + switch t.Size { + default: + fatalf("unexpected: %d-byte float type - %s", t.Size, dtype) + case 4: + t.Go = c.float32 + case 8: + t.Go = c.float64 + } + if t.Align = t.Size; t.Align >= c.ptrSize { + t.Align = c.ptrSize + } + + case *dwarf.ComplexType: + switch t.Size { + default: + fatalf("unexpected: %d-byte complex type - %s", t.Size, dtype) + case 8: + t.Go = c.complex64 + case 16: + t.Go = c.complex128 + } + if t.Align = t.Size; t.Align >= c.ptrSize { + t.Align = c.ptrSize + } + + case *dwarf.FuncType: + // No attempt at translation: would enable calls + // directly between worlds, but we need to moderate those. + t.Go = c.uintptr + t.Align = c.ptrSize + + case *dwarf.IntType: + if dt.BitSize > 0 { + fatalf("unexpected: %d-bit int type - %s", dt.BitSize, dtype) + } + switch t.Size { + default: + fatalf("unexpected: %d-byte int type - %s", t.Size, dtype) + case 1: + t.Go = c.int8 + case 2: + t.Go = c.int16 + case 4: + t.Go = c.int32 + case 8: + t.Go = c.int64 + } + if t.Align = t.Size; t.Align >= c.ptrSize { + t.Align = c.ptrSize + } + + case *dwarf.PtrType: + t.Align = c.ptrSize + + // Translate void* as unsafe.Pointer + if _, ok := base(dt.Type).(*dwarf.VoidType); ok { + t.Go = c.unsafePointer + t.C.Set("void*") + break + } + + gt := &ast.StarExpr{} + t.Go = gt // publish before recursive call + sub := c.Type(dt.Type) + gt.X = sub.Go + t.C.Set("%s*", sub.C) + + case *dwarf.QualType: + // Ignore qualifier. + t = c.Type(dt.Type) + c.m[dtype] = t + return t + + case *dwarf.StructType: + // Convert to Go struct, being careful about alignment. + // Have to give it a name to simulate C "struct foo" references. + tag := dt.StructName + if tag == "" { + tag = "__" + strconv.Itoa(tagGen) + tagGen++ + } else if t.C.Empty() { + t.C.Set(dt.Kind + " " + tag) + } + name := c.Ident("_Ctype_" + dt.Kind + "_" + tag) + t.Go = name // publish before recursive calls + switch dt.Kind { + case "union", "class": + typedef[name.Name] = c.Opaque(t.Size) + if t.C.Empty() { + t.C.Set("typeof(unsigned char[%d])", t.Size) + } + case "struct": + g, csyntax, align := c.Struct(dt) + if t.C.Empty() { + t.C.Set(csyntax) + } + t.Align = align + typedef[name.Name] = g + } + + case *dwarf.TypedefType: + // Record typedef for printing. + if dt.Name == "_GoString_" { + // Special C name for Go string type. + // Knows string layout used by compilers: pointer plus length, + // which rounds up to 2 pointers after alignment. + t.Go = c.string + t.Size = c.ptrSize * 2 + t.Align = c.ptrSize + break + } + if dt.Name == "_GoBytes_" { + // Special C name for Go []byte type. + // Knows slice layout used by compilers: pointer, length, cap. + t.Go = c.Ident("[]byte") + t.Size = c.ptrSize + 4 + 4 + t.Align = c.ptrSize + break + } + name := c.Ident("_Ctypedef_" + dt.Name) + t.Go = name // publish before recursive call + sub := c.Type(dt.Type) + t.Size = sub.Size + t.Align = sub.Align + if _, ok := typedef[name.Name]; !ok { + typedef[name.Name] = sub.Go + } + + case *dwarf.UcharType: + if t.Size != 1 { + fatalf("unexpected: %d-byte uchar type - %s", t.Size, dtype) + } + t.Go = c.uint8 + t.Align = 1 + + case *dwarf.UintType: + if dt.BitSize > 0 { + fatalf("unexpected: %d-bit uint type - %s", dt.BitSize, dtype) + } + switch t.Size { + default: + fatalf("unexpected: %d-byte uint type - %s", t.Size, dtype) + case 1: + t.Go = c.uint8 + case 2: + t.Go = c.uint16 + case 4: + t.Go = c.uint32 + case 8: + t.Go = c.uint64 + } + if t.Align = t.Size; t.Align >= c.ptrSize { + t.Align = c.ptrSize + } + + case *dwarf.VoidType: + t.Go = c.void + t.C.Set("void") + } + + switch dtype.(type) { + case *dwarf.AddrType, *dwarf.BoolType, *dwarf.CharType, *dwarf.IntType, *dwarf.FloatType, *dwarf.UcharType, *dwarf.UintType: + s := dtype.Common().Name + if s != "" { + if ss, ok := dwarfToName[s]; ok { + s = ss + } + s = strings.Join(strings.Split(s, " "), "") // strip spaces + name := c.Ident("_Ctype_" + s) + typedef[name.Name] = t.Go + t.Go = name + } + } + + if t.C.Empty() { + fatalf("internal error: did not create C name for %s", dtype) + } + + return t +} + +// FuncArg returns a Go type with the same memory layout as +// dtype when used as the type of a C function argument. +func (c *typeConv) FuncArg(dtype dwarf.Type) *Type { + t := c.Type(dtype) + switch dt := dtype.(type) { + case *dwarf.ArrayType: + // Arrays are passed implicitly as pointers in C. + // In Go, we must be explicit. + tr := &TypeRepr{} + tr.Set("%s*", t.C) + return &Type{ + Size: c.ptrSize, + Align: c.ptrSize, + Go: &ast.StarExpr{X: t.Go}, + C: tr, + } + case *dwarf.TypedefType: + // C has much more relaxed rules than Go for + // implicit type conversions. When the parameter + // is type T defined as *X, simulate a little of the + // laxness of C by making the argument *X instead of T. + if ptr, ok := base(dt.Type).(*dwarf.PtrType); ok { + // Unless the typedef happens to point to void* since + // Go has special rules around using unsafe.Pointer. + if _, void := base(ptr.Type).(*dwarf.VoidType); !void { + return c.Type(ptr) + } + } + } + return t +} + +// FuncType returns the Go type analogous to dtype. +// There is no guarantee about matching memory layout. +func (c *typeConv) FuncType(dtype *dwarf.FuncType) *FuncType { + p := make([]*Type, len(dtype.ParamType)) + gp := make([]*ast.Field, len(dtype.ParamType)) + for i, f := range dtype.ParamType { + // gcc's DWARF generator outputs a single DotDotDotType parameter for + // function pointers that specify no parameters (e.g. void + // (*__cgo_0)()). Treat this special case as void. This case is + // invalid according to ISO C anyway (i.e. void (*__cgo_1)(...) is not + // legal). + if _, ok := f.(*dwarf.DotDotDotType); ok && i == 0 { + p, gp = nil, nil + break + } + p[i] = c.FuncArg(f) + gp[i] = &ast.Field{Type: p[i].Go} + } + var r *Type + var gr []*ast.Field + if _, ok := dtype.ReturnType.(*dwarf.VoidType); !ok && dtype.ReturnType != nil { + r = c.Type(dtype.ReturnType) + gr = []*ast.Field{&ast.Field{Type: r.Go}} + } + return &FuncType{ + Params: p, + Result: r, + Go: &ast.FuncType{ + Params: &ast.FieldList{List: gp}, + Results: &ast.FieldList{List: gr}, + }, + } +} + +// Identifier +func (c *typeConv) Ident(s string) *ast.Ident { + return ast.NewIdent(s) +} + +// Opaque type of n bytes. +func (c *typeConv) Opaque(n int64) ast.Expr { + return &ast.ArrayType{ + Len: c.intExpr(n), + Elt: c.byte, + } +} + +// Expr for integer n. +func (c *typeConv) intExpr(n int64) ast.Expr { + return &ast.BasicLit{ + Kind: token.INT, + Value: strconv.Itoa64(n), + } +} + +// Add padding of given size to fld. +func (c *typeConv) pad(fld []*ast.Field, size int64) []*ast.Field { + n := len(fld) + fld = fld[0 : n+1] + fld[n] = &ast.Field{Names: []*ast.Ident{c.Ident("_")}, Type: c.Opaque(size)} + return fld +} + +// Struct conversion: return Go and (6g) C syntax for type. +func (c *typeConv) Struct(dt *dwarf.StructType) (expr *ast.StructType, csyntax string, align int64) { + var buf bytes.Buffer + buf.WriteString("struct {") + fld := make([]*ast.Field, 0, 2*len(dt.Field)+1) // enough for padding around every field + off := int64(0) + + // Rename struct fields that happen to be named Go keywords into + // _{keyword}. Create a map from C ident -> Go ident. The Go ident will + // be mangled. Any existing identifier that already has the same name on + // the C-side will cause the Go-mangled version to be prefixed with _. + // (e.g. in a struct with fields '_type' and 'type', the latter would be + // rendered as '__type' in Go). + ident := make(map[string]string) + used := make(map[string]bool) + for _, f := range dt.Field { + ident[f.Name] = f.Name + used[f.Name] = true + } + for cid, goid := range ident { + if token.Lookup([]byte(goid)).IsKeyword() { + // Avoid keyword + goid = "_" + goid + + // Also avoid existing fields + for _, exist := used[goid]; exist; _, exist = used[goid] { + goid = "_" + goid + } + + used[goid] = true + ident[cid] = goid + } + } + + for _, f := range dt.Field { + if f.BitSize > 0 && f.BitSize != f.ByteSize*8 { + continue + } + if f.ByteOffset > off { + fld = c.pad(fld, f.ByteOffset-off) + off = f.ByteOffset + } + t := c.Type(f.Type) + n := len(fld) + fld = fld[0 : n+1] + + fld[n] = &ast.Field{Names: []*ast.Ident{c.Ident(ident[f.Name])}, Type: t.Go} + off += t.Size + buf.WriteString(t.C.String()) + buf.WriteString(" ") + buf.WriteString(f.Name) + buf.WriteString("; ") + if t.Align > align { + align = t.Align + } + } + if off < dt.ByteSize { + fld = c.pad(fld, dt.ByteSize-off) + off = dt.ByteSize + } + if off != dt.ByteSize { + fatalf("struct size calculation error") + } + buf.WriteString("}") + csyntax = buf.String() + expr = &ast.StructType{Fields: &ast.FieldList{List: fld}} + return +} diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go new file mode 100644 index 000000000..be9c2bc4f --- /dev/null +++ b/src/cmd/cgo/main.go @@ -0,0 +1,268 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Cgo; see gmp.go for an overview. + +// TODO(rsc): +// Emit correct line number annotations. +// Make 6g understand the annotations. + +package main + +import ( + "crypto/md5" + "flag" + "fmt" + "go/ast" + "go/token" + "io" + "os" + "path/filepath" + "reflect" + "strings" +) + +// A Package collects information about the package we're going to write. +type Package struct { + PackageName string // name of package + PackagePath string + PtrSize int64 + GccOptions []string + CgoFlags map[string]string // #cgo flags (CFLAGS, LDFLAGS) + Written map[string]bool + Name map[string]*Name // accumulated Name from Files + Typedef map[string]ast.Expr // accumulated Typedef from Files + ExpFunc []*ExpFunc // accumulated ExpFunc from Files + Decl []ast.Decl + GoFiles []string // list of Go files + GccFiles []string // list of gcc output files +} + +// A File collects information about a single Go input file. +type File struct { + AST *ast.File // parsed AST + Package string // Package name + Preamble string // C preamble (doc comment on import "C") + Ref []*Ref // all references to C.xxx in AST + ExpFunc []*ExpFunc // exported functions for this file + Name map[string]*Name // map from Go name to Name + Typedef map[string]ast.Expr // translations of all necessary types from C +} + +// A Ref refers to an expression of the form C.xxx in the AST. +type Ref struct { + Name *Name + Expr *ast.Expr + Context string // "type", "expr", "call", or "call2" +} + +func (r *Ref) Pos() token.Pos { + return (*r.Expr).Pos() +} + +// A Name collects information about C.xxx. +type Name struct { + Go string // name used in Go referring to package C + Mangle string // name used in generated Go + C string // name used in C + Define string // #define expansion + Kind string // "const", "type", "var", "func", "not-type" + Type *Type // the type of xxx + FuncType *FuncType + AddError bool + Const string // constant definition +} + +// A ExpFunc is an exported function, callable from C. +// Such functions are identified in the Go input file +// by doc comments containing the line //export ExpName +type ExpFunc struct { + Func *ast.FuncDecl + ExpName string // name to use from C +} + +// A TypeRepr contains the string representation of a type. +type TypeRepr struct { + Repr string + FormatArgs []interface{} +} + +// A Type collects information about a type in both the C and Go worlds. +type Type struct { + Size int64 + Align int64 + C *TypeRepr + Go ast.Expr + EnumValues map[string]int64 +} + +// A FuncType collects information about a function type in both the C and Go worlds. +type FuncType struct { + Params []*Type + Result *Type + Go *ast.FuncType +} + +func usage() { + fmt.Fprint(os.Stderr, "usage: cgo -- [compiler options] file.go ...\n") + flag.PrintDefaults() + os.Exit(2) +} + +var ptrSizeMap = map[string]int64{ + "386": 4, + "amd64": 8, + "arm": 4, +} + +var cPrefix string + +var fset = token.NewFileSet() + +var dynobj = flag.String("dynimport", "", "if non-empty, print dynamic import data for that file") + +func main() { + flag.Usage = usage + flag.Parse() + + if *dynobj != "" { + // cgo -dynimport is essentially a separate helper command + // built into the cgo binary. It scans a gcc-produced executable + // and dumps information about the imported symbols and the + // imported libraries. The Make.pkg rules for cgo prepare an + // appropriate executable and then use its import information + // instead of needing to make the linkers duplicate all the + // specialized knowledge gcc has about where to look for imported + // symbols and which ones to use. + dynimport(*dynobj) + return + } + + args := flag.Args() + if len(args) < 1 { + usage() + } + + // Find first arg that looks like a go file and assume everything before + // that are options to pass to gcc. + var i int + for i = len(args); i > 0; i-- { + if !strings.HasSuffix(args[i-1], ".go") { + break + } + } + if i == len(args) { + usage() + } + + // Copy it to a new slice so it can grow. + gccOptions := make([]string, i) + copy(gccOptions, args[0:i]) + + goFiles := args[i:] + + arch := os.Getenv("GOARCH") + if arch == "" { + fatalf("$GOARCH is not set") + } + ptrSize := ptrSizeMap[arch] + if ptrSize == 0 { + fatalf("unknown $GOARCH %q", arch) + } + + // Clear locale variables so gcc emits English errors [sic]. + os.Setenv("LANG", "en_US.UTF-8") + os.Setenv("LC_ALL", "C") + os.Setenv("LC_CTYPE", "C") + + p := &Package{ + PtrSize: ptrSize, + GccOptions: gccOptions, + CgoFlags: make(map[string]string), + Written: make(map[string]bool), + } + + // Need a unique prefix for the global C symbols that + // we use to coordinate between gcc and ourselves. + // We already put _cgo_ at the beginning, so the main + // concern is other cgo wrappers for the same functions. + // Use the beginning of the md5 of the input to disambiguate. + h := md5.New() + for _, input := range goFiles { + f, err := os.Open(input) + if err != nil { + fatalf("%s", err) + } + io.Copy(h, f) + f.Close() + } + cPrefix = fmt.Sprintf("_%x", h.Sum()[0:6]) + + fs := make([]*File, len(goFiles)) + for i, input := range goFiles { + // Parse flags for all files before translating due to CFLAGS. + f := new(File) + f.ReadGo(input) + p.ParseFlags(f, input) + fs[i] = f + } + + // make sure that _obj directory exists, so that we can write + // all the output files there. + os.Mkdir("_obj", 0777) + + for i, input := range goFiles { + f := fs[i] + p.Translate(f) + for _, cref := range f.Ref { + switch cref.Context { + case "call", "call2": + if cref.Name.Kind != "type" { + break + } + *cref.Expr = cref.Name.Type.Go + } + } + if nerrors > 0 { + os.Exit(2) + } + pkg := f.Package + if dir := os.Getenv("CGOPKGPATH"); dir != "" { + pkg = filepath.Join(dir, pkg) + } + p.PackagePath = pkg + p.writeOutput(f, input) + + p.Record(f) + } + + p.writeDefs() + if nerrors > 0 { + os.Exit(2) + } +} + +// Record what needs to be recorded about f. +func (p *Package) Record(f *File) { + if p.PackageName == "" { + p.PackageName = f.Package + } else if p.PackageName != f.Package { + error(token.NoPos, "inconsistent package names: %s, %s", p.PackageName, f.Package) + } + + if p.Name == nil { + p.Name = f.Name + } else { + for k, v := range f.Name { + if p.Name[k] == nil { + p.Name[k] = v + } else if !reflect.DeepEqual(p.Name[k], v) { + error(token.NoPos, "inconsistent definitions for C.%s", k) + } + } + } + + p.ExpFunc = append(p.ExpFunc, f.ExpFunc...) + p.Decl = append(p.Decl, f.AST.Decls...) +} diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go new file mode 100644 index 000000000..498ab1566 --- /dev/null +++ b/src/cmd/cgo/out.go @@ -0,0 +1,745 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "bytes" + "debug/elf" + "debug/macho" + "debug/pe" + "fmt" + "go/ast" + "go/printer" + "go/token" + "os" + "path/filepath" + "strings" +) + +var objDir = "_obj" + string(filepath.Separator) + +// writeDefs creates output files to be compiled by 6g, 6c, and gcc. +// (The comments here say 6g and 6c but the code applies to the 8 and 5 tools too.) +func (p *Package) writeDefs() { + fgo2 := creat(objDir + "_cgo_gotypes.go") + fc := creat(objDir + "_cgo_defun.c") + fm := creat(objDir + "_cgo_main.c") + + fflg := creat(objDir + "_cgo_flags") + for k, v := range p.CgoFlags { + fmt.Fprintf(fflg, "_CGO_%s=%s\n", k, v) + } + fflg.Close() + + // Write C main file for using gcc to resolve imports. + fmt.Fprintf(fm, "int main() { return 0; }\n") + fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*, int), void *a, int c) { }\n") + fmt.Fprintf(fm, "void _cgo_allocate(void *a, int c) { }\n") + fmt.Fprintf(fm, "void _cgo_panic(void *a, int c) { }\n") + + // Write second Go output: definitions of _C_xxx. + // In a separate file so that the import of "unsafe" does not + // pollute the original file. + fmt.Fprintf(fgo2, "// Created by cgo - DO NOT EDIT\n\n") + fmt.Fprintf(fgo2, "package %s\n\n", p.PackageName) + fmt.Fprintf(fgo2, "import \"unsafe\"\n\n") + fmt.Fprintf(fgo2, "import \"os\"\n\n") + fmt.Fprintf(fgo2, "import _ \"runtime/cgo\"\n\n") + fmt.Fprintf(fgo2, "type _ unsafe.Pointer\n\n") + fmt.Fprintf(fgo2, "func _Cerrno(dst *os.Error, x int) { *dst = os.Errno(x) }\n") + + for name, def := range typedef { + fmt.Fprintf(fgo2, "type %s ", name) + printer.Fprint(fgo2, fset, def) + fmt.Fprintf(fgo2, "\n") + } + fmt.Fprintf(fgo2, "type _Ctype_void [0]byte\n") + + fmt.Fprintf(fc, cProlog) + + cVars := make(map[string]bool) + for _, n := range p.Name { + if n.Kind != "var" { + continue + } + + if !cVars[n.C] { + fmt.Fprintf(fm, "extern char %s[];\n", n.C) + fmt.Fprintf(fm, "void *_cgohack_%s = %s;\n\n", n.C, n.C) + + fmt.Fprintf(fc, "extern byte *%s;\n", n.C) + + cVars[n.C] = true + } + + fmt.Fprintf(fc, "void *·%s = &%s;\n", n.Mangle, n.C) + fmt.Fprintf(fc, "\n") + + fmt.Fprintf(fgo2, "var %s ", n.Mangle) + printer.Fprint(fgo2, fset, &ast.StarExpr{X: n.Type.Go}) + fmt.Fprintf(fgo2, "\n") + } + fmt.Fprintf(fc, "\n") + + for _, n := range p.Name { + if n.Const != "" { + fmt.Fprintf(fgo2, "const _Cconst_%s = %s\n", n.Go, n.Const) + } + } + fmt.Fprintf(fgo2, "\n") + + for _, n := range p.Name { + if n.FuncType != nil { + p.writeDefsFunc(fc, fgo2, n) + } + } + + p.writeExports(fgo2, fc, fm) + + fgo2.Close() + fc.Close() +} + +func dynimport(obj string) { + if f, err := elf.Open(obj); err == nil { + sym, err := f.ImportedSymbols() + if err != nil { + fatalf("cannot load imported symbols from ELF file %s: %v", obj, err) + } + for _, s := range sym { + targ := s.Name + if s.Version != "" { + targ += "@" + s.Version + } + fmt.Printf("#pragma dynimport %s %s %q\n", s.Name, targ, s.Library) + } + lib, err := f.ImportedLibraries() + if err != nil { + fatalf("cannot load imported libraries from ELF file %s: %v", obj, err) + } + for _, l := range lib { + fmt.Printf("#pragma dynimport _ _ %q\n", l) + } + return + } + + if f, err := macho.Open(obj); err == nil { + sym, err := f.ImportedSymbols() + if err != nil { + fatalf("cannot load imported symbols from Mach-O file %s: %v", obj, err) + } + for _, s := range sym { + if len(s) > 0 && s[0] == '_' { + s = s[1:] + } + fmt.Printf("#pragma dynimport %s %s %q\n", s, s, "") + } + lib, err := f.ImportedLibraries() + if err != nil { + fatalf("cannot load imported libraries from Mach-O file %s: %v", obj, err) + } + for _, l := range lib { + fmt.Printf("#pragma dynimport _ _ %q\n", l) + } + return + } + + if f, err := pe.Open(obj); err == nil { + sym, err := f.ImportedSymbols() + if err != nil { + fatalf("cannot load imported symbols from PE file %s: %v", obj, err) + } + for _, s := range sym { + ss := strings.Split(s, ":") + fmt.Printf("#pragma dynimport %s %s %q\n", ss[0], ss[0], strings.ToLower(ss[1])) + } + return + } + + fatalf("cannot parse %s as ELF, Mach-O or PE", obj) +} + +// Construct a gcc struct matching the 6c argument frame. +// Assumes that in gcc, char is 1 byte, short 2 bytes, int 4 bytes, long long 8 bytes. +// These assumptions are checked by the gccProlog. +// Also assumes that 6c convention is to word-align the +// input and output parameters. +func (p *Package) structType(n *Name) (string, int64) { + var buf bytes.Buffer + fmt.Fprint(&buf, "struct {\n") + off := int64(0) + for i, t := range n.FuncType.Params { + if off%t.Align != 0 { + pad := t.Align - off%t.Align + fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad) + off += pad + } + fmt.Fprintf(&buf, "\t\t%s p%d;\n", t.C, i) + off += t.Size + } + if off%p.PtrSize != 0 { + pad := p.PtrSize - off%p.PtrSize + fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad) + off += pad + } + if t := n.FuncType.Result; t != nil { + if off%t.Align != 0 { + pad := t.Align - off%t.Align + fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad) + off += pad + } + qual := "" + if c := t.C.String(); c[len(c)-1] == '*' { + qual = "const " + } + fmt.Fprintf(&buf, "\t\t%s%s r;\n", qual, t.C) + off += t.Size + } + if off%p.PtrSize != 0 { + pad := p.PtrSize - off%p.PtrSize + fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad) + off += pad + } + if n.AddError { + fmt.Fprint(&buf, "\t\tvoid *e[2]; /* os.Error */\n") + off += 2 * p.PtrSize + } + if off == 0 { + fmt.Fprintf(&buf, "\t\tchar unused;\n") // avoid empty struct + } + fmt.Fprintf(&buf, "\t}") + return buf.String(), off +} + +func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) { + name := n.Go + gtype := n.FuncType.Go + if n.AddError { + // Add "os.Error" to return type list. + // Type list is known to be 0 or 1 element - it's a C function. + err := &ast.Field{Type: ast.NewIdent("os.Error")} + l := gtype.Results.List + if len(l) == 0 { + l = []*ast.Field{err} + } else { + l = []*ast.Field{l[0], err} + } + t := new(ast.FuncType) + *t = *gtype + t.Results = &ast.FieldList{List: l} + gtype = t + } + + // Go func declaration. + d := &ast.FuncDecl{ + Name: ast.NewIdent(n.Mangle), + Type: gtype, + } + printer.Fprint(fgo2, fset, d) + fmt.Fprintf(fgo2, "\n") + + if name == "CString" || name == "GoString" || name == "GoStringN" || name == "GoBytes" { + // The builtins are already defined in the C prolog. + return + } + + var argSize int64 + _, argSize = p.structType(n) + + // C wrapper calls into gcc, passing a pointer to the argument frame. + fmt.Fprintf(fc, "void _cgo%s%s(void*);\n", cPrefix, n.Mangle) + fmt.Fprintf(fc, "\n") + fmt.Fprintf(fc, "void\n") + if argSize == 0 { + argSize++ + } + fmt.Fprintf(fc, "·%s(struct{uint8 x[%d];}p)\n", n.Mangle, argSize) + fmt.Fprintf(fc, "{\n") + fmt.Fprintf(fc, "\truntime·cgocall(_cgo%s%s, &p);\n", cPrefix, n.Mangle) + if n.AddError { + // gcc leaves errno in first word of interface at end of p. + // check whether it is zero; if so, turn interface into nil. + // if not, turn interface into errno. + // Go init function initializes ·_Cerrno with an os.Errno + // for us to copy. + fmt.Fprintln(fc, ` { + int32 e; + void **v; + v = (void**)(&p+1) - 2; /* v = final two void* of p */ + e = *(int32*)v; + v[0] = (void*)0xdeadbeef; + v[1] = (void*)0xdeadbeef; + if(e == 0) { + /* nil interface */ + v[0] = 0; + v[1] = 0; + } else { + ·_Cerrno(v, e); /* fill in v as os.Error for errno e */ + } + }`) + } + fmt.Fprintf(fc, "}\n") + fmt.Fprintf(fc, "\n") +} + +// writeOutput creates stubs for a specific source file to be compiled by 6g +// (The comments here say 6g and 6c but the code applies to the 8 and 5 tools too.) +func (p *Package) writeOutput(f *File, srcfile string) { + base := srcfile + if strings.HasSuffix(base, ".go") { + base = base[0 : len(base)-3] + } + base = strings.Map(slashToUnderscore, base) + fgo1 := creat(objDir + base + ".cgo1.go") + fgcc := creat(objDir + base + ".cgo2.c") + + p.GoFiles = append(p.GoFiles, base+".cgo1.go") + p.GccFiles = append(p.GccFiles, base+".cgo2.c") + + // Write Go output: Go input with rewrites of C.xxx to _C_xxx. + fmt.Fprintf(fgo1, "// Created by cgo - DO NOT EDIT\n\n") + fmt.Fprintf(fgo1, "//line %s:1\n", srcfile) + printer.Fprint(fgo1, fset, f.AST) + + // While we process the vars and funcs, also write 6c and gcc output. + // Gcc output starts with the preamble. + fmt.Fprintf(fgcc, "%s\n", f.Preamble) + fmt.Fprintf(fgcc, "%s\n", gccProlog) + + for _, n := range f.Name { + if n.FuncType != nil { + p.writeOutputFunc(fgcc, n) + } + } + + fgo1.Close() + fgcc.Close() +} + +func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) { + name := n.Mangle + if name == "_Cfunc_CString" || name == "_Cfunc_GoString" || name == "_Cfunc_GoStringN" || name == "_Cfunc_GoBytes" || p.Written[name] { + // The builtins are already defined in the C prolog, and we don't + // want to duplicate function definitions we've already done. + return + } + p.Written[name] = true + + ctype, _ := p.structType(n) + + // Gcc wrapper unpacks the C argument struct + // and calls the actual C function. + fmt.Fprintf(fgcc, "void\n") + fmt.Fprintf(fgcc, "_cgo%s%s(void *v)\n", cPrefix, n.Mangle) + fmt.Fprintf(fgcc, "{\n") + if n.AddError { + fmt.Fprintf(fgcc, "\tint e;\n") // assuming 32 bit (see comment above structType) + fmt.Fprintf(fgcc, "\terrno = 0;\n") + } + // We're trying to write a gcc struct that matches 6c/8c/5c's layout. + // Use packed attribute to force no padding in this struct in case + // gcc has different packing requirements. For example, + // on 386 Windows, gcc wants to 8-align int64s, but 8c does not. + fmt.Fprintf(fgcc, "\t%s __attribute__((__packed__)) *a = v;\n", ctype) + fmt.Fprintf(fgcc, "\t") + if t := n.FuncType.Result; t != nil { + fmt.Fprintf(fgcc, "a->r = ") + if c := t.C.String(); c[len(c)-1] == '*' { + fmt.Fprintf(fgcc, "(const %s) ", t.C) + } + } + fmt.Fprintf(fgcc, "%s(", n.C) + for i := range n.FuncType.Params { + if i > 0 { + fmt.Fprintf(fgcc, ", ") + } + fmt.Fprintf(fgcc, "a->p%d", i) + } + fmt.Fprintf(fgcc, ");\n") + if n.AddError { + fmt.Fprintf(fgcc, "\t*(int*)(a->e) = errno;\n") + } + fmt.Fprintf(fgcc, "}\n") + fmt.Fprintf(fgcc, "\n") +} + +// Write out the various stubs we need to support functions exported +// from Go so that they are callable from C. +func (p *Package) writeExports(fgo2, fc, fm *os.File) { + fgcc := creat(objDir + "_cgo_export.c") + fgcch := creat("_cgo_export.h") + + fmt.Fprintf(fgcch, "/* Created by cgo - DO NOT EDIT. */\n") + fmt.Fprintf(fgcch, "%s\n", gccExportHeaderProlog) + + fmt.Fprintf(fgcc, "/* Created by cgo - DO NOT EDIT. */\n") + fmt.Fprintf(fgcc, "#include \"_cgo_export.h\"\n") + + for _, exp := range p.ExpFunc { + fn := exp.Func + + // Construct a gcc struct matching the 6c argument and + // result frame. The gcc struct will be compiled with + // __attribute__((packed)) so all padding must be accounted + // for explicitly. + ctype := "struct {\n" + off := int64(0) + npad := 0 + if fn.Recv != nil { + t := p.cgoType(fn.Recv.List[0].Type) + ctype += fmt.Sprintf("\t\t%s recv;\n", t.C) + off += t.Size + } + fntype := fn.Type + forFieldList(fntype.Params, + func(i int, atype ast.Expr) { + t := p.cgoType(atype) + if off%t.Align != 0 { + pad := t.Align - off%t.Align + ctype += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) + off += pad + npad++ + } + ctype += fmt.Sprintf("\t\t%s p%d;\n", t.C, i) + off += t.Size + }) + if off%p.PtrSize != 0 { + pad := p.PtrSize - off%p.PtrSize + ctype += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) + off += pad + npad++ + } + forFieldList(fntype.Results, + func(i int, atype ast.Expr) { + t := p.cgoType(atype) + if off%t.Align != 0 { + pad := t.Align - off%t.Align + ctype += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) + off += pad + npad++ + } + ctype += fmt.Sprintf("\t\t%s r%d;\n", t.C, i) + off += t.Size + }) + if off%p.PtrSize != 0 { + pad := p.PtrSize - off%p.PtrSize + ctype += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) + off += pad + npad++ + } + if ctype == "struct {\n" { + ctype += "\t\tchar unused;\n" // avoid empty struct + } + ctype += "\t}" + + // Get the return type of the wrapper function + // compiled by gcc. + gccResult := "" + if fntype.Results == nil || len(fntype.Results.List) == 0 { + gccResult = "void" + } else if len(fntype.Results.List) == 1 && len(fntype.Results.List[0].Names) <= 1 { + gccResult = p.cgoType(fntype.Results.List[0].Type).C.String() + } else { + fmt.Fprintf(fgcch, "\n/* Return type for %s */\n", exp.ExpName) + fmt.Fprintf(fgcch, "struct %s_return {\n", exp.ExpName) + forFieldList(fntype.Results, + func(i int, atype ast.Expr) { + fmt.Fprintf(fgcch, "\t%s r%d;\n", p.cgoType(atype).C, i) + }) + fmt.Fprintf(fgcch, "};\n") + gccResult = "struct " + exp.ExpName + "_return" + } + + // Build the wrapper function compiled by gcc. + s := fmt.Sprintf("%s %s(", gccResult, exp.ExpName) + if fn.Recv != nil { + s += p.cgoType(fn.Recv.List[0].Type).C.String() + s += " recv" + } + forFieldList(fntype.Params, + func(i int, atype ast.Expr) { + if i > 0 || fn.Recv != nil { + s += ", " + } + s += fmt.Sprintf("%s p%d", p.cgoType(atype).C, i) + }) + s += ")" + fmt.Fprintf(fgcch, "\nextern %s;\n", s) + + fmt.Fprintf(fgcc, "extern _cgoexp%s_%s(void *, int);\n", cPrefix, exp.ExpName) + fmt.Fprintf(fgcc, "\n%s\n", s) + fmt.Fprintf(fgcc, "{\n") + fmt.Fprintf(fgcc, "\t%s __attribute__((packed)) a;\n", ctype) + if gccResult != "void" && (len(fntype.Results.List) > 1 || len(fntype.Results.List[0].Names) > 1) { + fmt.Fprintf(fgcc, "\t%s r;\n", gccResult) + } + if fn.Recv != nil { + fmt.Fprintf(fgcc, "\ta.recv = recv;\n") + } + forFieldList(fntype.Params, + func(i int, atype ast.Expr) { + fmt.Fprintf(fgcc, "\ta.p%d = p%d;\n", i, i) + }) + fmt.Fprintf(fgcc, "\tcrosscall2(_cgoexp%s_%s, &a, %d);\n", cPrefix, exp.ExpName, off) + if gccResult != "void" { + if len(fntype.Results.List) == 1 && len(fntype.Results.List[0].Names) <= 1 { + fmt.Fprintf(fgcc, "\treturn a.r0;\n") + } else { + forFieldList(fntype.Results, + func(i int, atype ast.Expr) { + fmt.Fprintf(fgcc, "\tr.r%d = a.r%d;\n", i, i) + }) + fmt.Fprintf(fgcc, "\treturn r;\n") + } + } + fmt.Fprintf(fgcc, "}\n") + + // Build the wrapper function compiled by 6c/8c + goname := exp.Func.Name.Name + if fn.Recv != nil { + goname = "_cgoexpwrap" + cPrefix + "_" + fn.Recv.List[0].Names[0].Name + "_" + goname + } + fmt.Fprintf(fc, "extern void ·%s();\n", goname) + fmt.Fprintf(fc, "\nvoid\n") + fmt.Fprintf(fc, "_cgoexp%s_%s(void *a, int32 n)\n", cPrefix, exp.ExpName) + fmt.Fprintf(fc, "{\n") + fmt.Fprintf(fc, "\truntime·cgocallback(·%s, a, n);\n", goname) + fmt.Fprintf(fc, "}\n") + + fmt.Fprintf(fm, "int _cgoexp%s_%s;\n", cPrefix, exp.ExpName) + + // Calling a function with a receiver from C requires + // a Go wrapper function. + if fn.Recv != nil { + fmt.Fprintf(fgo2, "func %s(recv ", goname) + printer.Fprint(fgo2, fset, fn.Recv.List[0].Type) + forFieldList(fntype.Params, + func(i int, atype ast.Expr) { + fmt.Fprintf(fgo2, ", p%d ", i) + printer.Fprint(fgo2, fset, atype) + }) + fmt.Fprintf(fgo2, ")") + if gccResult != "void" { + fmt.Fprint(fgo2, " (") + forFieldList(fntype.Results, + func(i int, atype ast.Expr) { + if i > 0 { + fmt.Fprint(fgo2, ", ") + } + printer.Fprint(fgo2, fset, atype) + }) + fmt.Fprint(fgo2, ")") + } + fmt.Fprint(fgo2, " {\n") + fmt.Fprint(fgo2, "\t") + if gccResult != "void" { + fmt.Fprint(fgo2, "return ") + } + fmt.Fprintf(fgo2, "recv.%s(", exp.Func.Name) + forFieldList(fntype.Params, + func(i int, atype ast.Expr) { + if i > 0 { + fmt.Fprint(fgo2, ", ") + } + fmt.Fprintf(fgo2, "p%d", i) + }) + fmt.Fprint(fgo2, ")\n") + fmt.Fprint(fgo2, "}\n") + } + } +} + +// Call a function for each entry in an ast.FieldList, passing the +// index into the list and the type. +func forFieldList(fl *ast.FieldList, fn func(int, ast.Expr)) { + if fl == nil { + return + } + i := 0 + for _, r := range fl.List { + if r.Names == nil { + fn(i, r.Type) + i++ + } else { + for _ = range r.Names { + fn(i, r.Type) + i++ + } + } + } +} + +func c(repr string, args ...interface{}) *TypeRepr { + return &TypeRepr{repr, args} +} + +// Map predeclared Go types to Type. +var goTypes = map[string]*Type{ + "int": &Type{Size: 4, Align: 4, C: c("int")}, + "uint": &Type{Size: 4, Align: 4, C: c("uint")}, + "int8": &Type{Size: 1, Align: 1, C: c("schar")}, + "uint8": &Type{Size: 1, Align: 1, C: c("uchar")}, + "int16": &Type{Size: 2, Align: 2, C: c("short")}, + "uint16": &Type{Size: 2, Align: 2, C: c("ushort")}, + "int32": &Type{Size: 4, Align: 4, C: c("int")}, + "uint32": &Type{Size: 4, Align: 4, C: c("uint")}, + "int64": &Type{Size: 8, Align: 8, C: c("int64")}, + "uint64": &Type{Size: 8, Align: 8, C: c("uint64")}, + "float": &Type{Size: 4, Align: 4, C: c("float")}, + "float32": &Type{Size: 4, Align: 4, C: c("float")}, + "float64": &Type{Size: 8, Align: 8, C: c("double")}, + "complex": &Type{Size: 8, Align: 8, C: c("__complex float")}, + "complex64": &Type{Size: 8, Align: 8, C: c("__complex float")}, + "complex128": &Type{Size: 16, Align: 16, C: c("__complex double")}, +} + +// Map an ast type to a Type. +func (p *Package) cgoType(e ast.Expr) *Type { + switch t := e.(type) { + case *ast.StarExpr: + x := p.cgoType(t.X) + return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("%s*", x.C)} + case *ast.ArrayType: + if t.Len == nil { + return &Type{Size: p.PtrSize + 8, Align: p.PtrSize, C: c("GoSlice")} + } + case *ast.StructType: + // TODO + case *ast.FuncType: + return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("void*")} + case *ast.InterfaceType: + return &Type{Size: 3 * p.PtrSize, Align: p.PtrSize, C: c("GoInterface")} + case *ast.MapType: + return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("GoMap")} + case *ast.ChanType: + return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("GoChan")} + case *ast.Ident: + // Look up the type in the top level declarations. + // TODO: Handle types defined within a function. + for _, d := range p.Decl { + gd, ok := d.(*ast.GenDecl) + if !ok || gd.Tok != token.TYPE { + continue + } + for _, spec := range gd.Specs { + ts, ok := spec.(*ast.TypeSpec) + if !ok { + continue + } + if ts.Name.Name == t.Name { + return p.cgoType(ts.Type) + } + } + } + for name, def := range typedef { + if name == t.Name { + return p.cgoType(def) + } + } + if t.Name == "uintptr" { + return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("uintptr")} + } + if t.Name == "string" { + return &Type{Size: p.PtrSize + 4, Align: p.PtrSize, C: c("GoString")} + } + if r, ok := goTypes[t.Name]; ok { + if r.Align > p.PtrSize { + r.Align = p.PtrSize + } + return r + } + error(e.Pos(), "unrecognized Go type %s", t.Name) + return &Type{Size: 4, Align: 4, C: c("int")} + case *ast.SelectorExpr: + id, ok := t.X.(*ast.Ident) + if ok && id.Name == "unsafe" && t.Sel.Name == "Pointer" { + return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("void*")} + } + } + error(e.Pos(), "unrecognized Go type %T", e) + return &Type{Size: 4, Align: 4, C: c("int")} +} + +const gccProlog = ` +// Usual nonsense: if x and y are not equal, the type will be invalid +// (have a negative array count) and an inscrutable error will come +// out of the compiler and hopefully mention "name". +#define __cgo_compile_assert_eq(x, y, name) typedef char name[(x-y)*(x-y)*-2+1]; + +// Check at compile time that the sizes we use match our expectations. +#define __cgo_size_assert(t, n) __cgo_compile_assert_eq(sizeof(t), n, _cgo_sizeof_##t##_is_not_##n) + +__cgo_size_assert(char, 1) +__cgo_size_assert(short, 2) +__cgo_size_assert(int, 4) +typedef long long __cgo_long_long; +__cgo_size_assert(__cgo_long_long, 8) +__cgo_size_assert(float, 4) +__cgo_size_assert(double, 8) + +#include <errno.h> +#include <string.h> +` + +const builtinProlog = ` +typedef struct { char *p; int n; } _GoString_; +typedef struct { char *p; int n; int c; } _GoBytes_; +_GoString_ GoString(char *p); +_GoString_ GoStringN(char *p, int l); +_GoBytes_ GoBytes(void *p, int n); +char *CString(_GoString_); +` + +const cProlog = ` +#include "runtime.h" +#include "cgocall.h" + +void ·_Cerrno(void*, int32); + +void +·_Cfunc_GoString(int8 *p, String s) +{ + s = runtime·gostring((byte*)p); + FLUSH(&s); +} + +void +·_Cfunc_GoStringN(int8 *p, int32 l, String s) +{ + s = runtime·gostringn((byte*)p, l); + FLUSH(&s); +} + +void +·_Cfunc_GoBytes(int8 *p, int32 l, Slice s) +{ + s = runtime·gobytes((byte*)p, l); + FLUSH(&s); +} + +void +·_Cfunc_CString(String s, int8 *p) +{ + p = runtime·cmalloc(s.len+1); + runtime·memmove((byte*)p, s.str, s.len); + p[s.len] = 0; + FLUSH(&p); +} +` + +const gccExportHeaderProlog = ` +typedef unsigned int uint; +typedef signed char schar; +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef long long int64; +typedef unsigned long long uint64; +typedef __SIZE_TYPE__ uintptr; + +typedef struct { char *p; int n; } GoString; +typedef void *GoMap; +typedef void *GoChan; +typedef struct { void *t; void *v; } GoInterface; +` diff --git a/src/cmd/cgo/util.go b/src/cmd/cgo/util.go new file mode 100644 index 000000000..e79b0e1bf --- /dev/null +++ b/src/cmd/cgo/util.go @@ -0,0 +1,110 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "exec" + "fmt" + "go/token" + "io/ioutil" + "os" +) + +// run runs the command argv, feeding in stdin on standard input. +// It returns the output to standard output and standard error. +// ok indicates whether the command exited successfully. +func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) { + cmd, err := exec.LookPath(argv[0]) + if err != nil { + fatalf("exec %s: %s", argv[0], err) + } + r0, w0, err := os.Pipe() + if err != nil { + fatalf("%s", err) + } + r1, w1, err := os.Pipe() + if err != nil { + fatalf("%s", err) + } + r2, w2, err := os.Pipe() + if err != nil { + fatalf("%s", err) + } + p, err := os.StartProcess(cmd, argv, &os.ProcAttr{Files: []*os.File{r0, w1, w2}}) + if err != nil { + fatalf("%s", err) + } + defer p.Release() + r0.Close() + w1.Close() + w2.Close() + c := make(chan bool) + go func() { + w0.Write(stdin) + w0.Close() + c <- true + }() + go func() { + stdout, _ = ioutil.ReadAll(r1) + r1.Close() + c <- true + }() + stderr, _ = ioutil.ReadAll(r2) + r2.Close() + <-c + <-c + + w, err := p.Wait(0) + if err != nil { + fatalf("%s", err) + } + ok = w.Exited() && w.ExitStatus() == 0 + return +} + +// Die with an error message. +func fatalf(msg string, args ...interface{}) { + fmt.Fprintf(os.Stderr, msg+"\n", args...) + os.Exit(2) +} + +var nerrors int + +func error(pos token.Pos, msg string, args ...interface{}) { + nerrors++ + if pos.IsValid() { + fmt.Fprintf(os.Stderr, "%s: ", fset.Position(pos).String()) + } + fmt.Fprintf(os.Stderr, msg, args...) + fmt.Fprintf(os.Stderr, "\n") +} + +// isName returns true if s is a valid C identifier +func isName(s string) bool { + for i, v := range s { + if v != '_' && (v < 'A' || v > 'Z') && (v < 'a' || v > 'z') && (v < '0' || v > '9') { + return false + } + if i == 0 && '0' <= v && v <= '9' { + return false + } + } + return s != "" +} + +func creat(name string) *os.File { + f, err := os.Create(name) + if err != nil { + fatalf("%s", err) + } + return f +} + +func slashToUnderscore(c int) int { + if c == '/' || c == '\\' || c == ':' { + c = '_' + } + return c +} |