From 6a2602de918047d35f3390131dabf0243e425bbc Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 18 Sep 2009 11:52:00 -0700 Subject: [PATCH] cgo: can look up C identifier kind (type or value) and type gmp.go:197:4: type mpz_t C type mpz_t gmp.go:205:2: call mpz_init C value func(mpz_ptr) void gmp.go:206:2: call mpz_set C value func(mpz_ptr, mpz_srcptr) void gmp.go:221:2: call mpz_init C value func(mpz_ptr) void gmp.go:227:7: call size_t C type size_t gmp.go:228:2: call mpz_export C value func(*void, *size_t, int, size_t, int, size_t, mpz_srcptr) *void gmp.go:235:13: call mpz_sizeinbase C value func(mpz_srcptr, int) size_t gmp.go:241:2: call mpz_set C value func(mpz_ptr, mpz_srcptr) void gmp.go:252:3: call mpz_import C value func(mpz_ptr, size_t, int, size_t, int, size_t, *const void) void gmp.go:261:2: call mpz_set_si C value func(mpz_ptr, long int) void gmp.go:273:5: call mpz_set_str C value func(mpz_ptr, *const char, int) int gmp.go:282:9: call mpz_get_str C value func(*char, int, mpz_srcptr) *char gmp.go:287:3: call mpz_clear C value func(mpz_ptr) void gmp.go:302:2: call mpz_add C value func(mpz_ptr, mpz_srcptr, mpz_srcptr) void gmp.go:311:2: call mpz_sub C value func(mpz_ptr, mpz_srcptr, mpz_srcptr) void gmp.go:320:2: call mpz_mul C value func(mpz_ptr, mpz_srcptr, mpz_srcptr) void gmp.go:329:2: call mpz_tdiv_q C value func(mpz_ptr, mpz_srcptr, mpz_srcptr) void gmp.go:339:2: call mpz_tdiv_r C value func(mpz_ptr, mpz_srcptr, mpz_srcptr) void gmp.go:348:2: call mpz_mul_2exp C value func(mpz_ptr, mpz_srcptr, long unsigned int) void gmp.go:356:2: call mpz_div_2exp C value func(mpz_ptr, mpz_srcptr, long unsigned int) void gmp.go:367:3: call mpz_pow_ui C value func(mpz_ptr, mpz_srcptr, long unsigned int) void gmp.go:369:3: call mpz_powm C value func(mpz_ptr, mpz_srcptr, mpz_srcptr, mpz_srcptr) void gmp.go:378:2: call mpz_neg C value func(mpz_ptr, mpz_srcptr) void gmp.go:386:2: call mpz_abs C value func(mpz_ptr, mpz_srcptr) void gmp.go:404:9: call mpz_cmp C value func(mpz_srcptr, mpz_srcptr) int gmp.go:413:2: call mpz_tdiv_qr C value func(mpz_ptr, mpz_ptr, mpz_srcptr, mpz_srcptr) void gmp.go:426:2: call mpz_gcdext C value func(mpz_ptr, mpz_ptr, mpz_ptr, mpz_srcptr, mpz_srcptr) void R=r DELTA=938 (628 added, 308 deleted, 2 changed) OCL=34733 CL=34791 --- src/cmd/cgo/Makefile | 5 +- src/cmd/cgo/ast.go | 288 ++++++++++++++++++++++++++++++++++++++++ src/cmd/cgo/cgo.go | 308 ------------------------------------------- src/cmd/cgo/gcc.go | 203 ++++++++++++++++++++++++++++ src/cmd/cgo/gmp.go | 2 +- src/cmd/cgo/main.go | 46 +++++++ src/cmd/cgo/util.go | 96 ++++++++++++++ 7 files changed, 638 insertions(+), 310 deletions(-) create mode 100644 src/cmd/cgo/ast.go delete mode 100644 src/cmd/cgo/cgo.go create mode 100644 src/cmd/cgo/gcc.go create mode 100644 src/cmd/cgo/main.go create mode 100644 src/cmd/cgo/util.go diff --git a/src/cmd/cgo/Makefile b/src/cmd/cgo/Makefile index 8d9e11e539..c7a2ed9c4c 100644 --- a/src/cmd/cgo/Makefile +++ b/src/cmd/cgo/Makefile @@ -6,6 +6,9 @@ include $(GOROOT)/src/Make.$(GOARCH) TARG=cgo GOFILES=\ - cgo.go\ + ast.go\ + gcc.go\ + main.go\ + util.go\ include $(GOROOT)/src/Make.cmd diff --git a/src/cmd/cgo/ast.go b/src/cmd/cgo/ast.go new file mode 100644 index 0000000000..a5f4995a11 --- /dev/null +++ b/src/cmd/cgo/ast.go @@ -0,0 +1,288 @@ +// 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 ( + "debug/dwarf"; + "fmt"; + "go/ast"; + "go/doc"; + "go/parser"; + "go/scanner"; + "os"; +) + +// A Cref refers to an expression of the form C.xxx in the AST. +type Cref struct { + Name string; + Expr *ast.Expr; + Context string; // "type", "expr", or "call" + TypeName bool; // whether xxx is a C type name + DebugType dwarf.Type; // the type of xxx +} + +// A Prog collects information about a cgo program. +type Prog struct { + AST *ast.File; // parsed AST + Preamble string; // C preamble (doc comment on import "C") + Crefs []*Cref; +} + +func openProg(name string) *Prog { + p := new(Prog); + var err os.Error; + p.AST, err = parser.ParsePkgFile("", name, parser.ParseComments); + 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); + } + fatal("parsing %s: %s", name, err); + } + + // Find the import "C" line and get any extra C preamble. + found := false; + for _, d := range p.AST.Decls { + d, ok := d.(*ast.GenDecl); + if !ok { + continue; + } + for _, s := range d.Specs { + s, ok := s.(*ast.ImportSpec); + if !ok { + continue; + } + if len(s.Path) != 1 || string(s.Path[0].Value) != `"C"` { + continue; + } + found = true; + if s.Name != nil { + error(s.Path[0].Pos(), `cannot rename import "C"`); + } + if s.Doc != nil { + p.Preamble += doc.CommentText(s.Doc) + "\n"; + } + else if len(d.Specs) == 1 && d.Doc != nil { + p.Preamble += doc.CommentText(d.Doc) + "\n"; + } + } + } + if !found { + error(noPos, `cannot find import "C"`); + } + + // Accumulate pointers to uses of C.x. + p.Crefs = make([]*Cref, 0, 8); + walk(p.AST, p, "prog"); + return p; +} + +func walk(x interface{}, p *Prog, context string) { + switch n := x.(type) { + case *ast.Expr: + 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.Value == "C" { + i := len(p.Crefs); + if i >= cap(p.Crefs) { + new := make([]*Cref, 2*i); + for j, v := range p.Crefs { + new[j] = v; + } + p.Crefs = new; + } + p.Crefs = p.Crefs[0:i+1]; + p.Crefs[i] = &Cref{ + Name: sel.Sel.Value, + Expr: n, + Context: context + }; + break; + } + } + walk(*n, p, context); + + // everything else just recurs + default: + error(noPos, "unexpected type %T in walk", x); + panic(); + + case nil: + + // These are ordered and grouped to match ../../pkg/go/ast/ast.go + case *ast.Field: + walk(&n.Type, p, "type"); + case *ast.BadExpr: + case *ast.Ident: + case *ast.Ellipsis: + case *ast.BasicLit: + case *ast.StringList: + case *ast.FuncLit: + walk(n.Type, p, "type"); + walk(n.Body, p, "stmt"); + case *ast.CompositeLit: + walk(&n.Type, p, "type"); + walk(n.Elts, p, "expr"); + case *ast.ParenExpr: + walk(&n.X, p, context); + case *ast.SelectorExpr: + walk(&n.X, p, "selector"); + case *ast.IndexExpr: + walk(&n.X, p, "expr"); + walk(&n.Index, p, "expr"); + if n.End != nil { + walk(&n.End, p, "expr"); + } + case *ast.TypeAssertExpr: + walk(&n.X, p, "expr"); + walk(&n.Type, p, "type"); + case *ast.CallExpr: + walk(&n.Fun, p, "call"); + walk(n.Args, p, "expr"); + case *ast.StarExpr: + walk(&n.X, p, context); + case *ast.UnaryExpr: + walk(&n.X, p, "expr"); + case *ast.BinaryExpr: + walk(&n.X, p, "expr"); + walk(&n.Y, p, "expr"); + case *ast.KeyValueExpr: + walk(&n.Key, p, "expr"); + walk(&n.Value, p, "expr"); + + case *ast.ArrayType: + walk(&n.Len, p, "expr"); + walk(&n.Elt, p, "type"); + case *ast.StructType: + walk(n.Fields, p, "field"); + case *ast.FuncType: + walk(n.Params, p, "field"); + walk(n.Results, p, "field"); + case *ast.InterfaceType: + walk(n.Methods, p, "field"); + case *ast.MapType: + walk(&n.Key, p, "type"); + walk(&n.Value, p, "type"); + case *ast.ChanType: + walk(&n.Value, p, "type"); + + case *ast.BadStmt: + case *ast.DeclStmt: + walk(n.Decl, p, "decl"); + case *ast.EmptyStmt: + case *ast.LabeledStmt: + walk(n.Stmt, p, "stmt"); + case *ast.ExprStmt: + walk(&n.X, p, "expr"); + case *ast.IncDecStmt: + walk(&n.X, p, "expr"); + case *ast.AssignStmt: + walk(n.Lhs, p, "expr"); + walk(n.Rhs, p, "expr"); + case *ast.GoStmt: + walk(&n.Call, p, "expr"); + case *ast.DeferStmt: + walk(&n.Call, p, "expr"); + case *ast.ReturnStmt: + walk(n.Results, p, "expr"); + case *ast.BranchStmt: + case *ast.BlockStmt: + walk(n.List, p, "stmt"); + case *ast.IfStmt: + walk(n.Init, p, "stmt"); + walk(&n.Cond, p, "expr"); + walk(n.Body, p, "stmt"); + walk(n.Else, p, "stmt"); + case *ast.CaseClause: + walk(n.Values, p, "expr"); + walk(n.Body, p, "stmt"); + case *ast.SwitchStmt: + walk(n.Init, p, "stmt"); + walk(&n.Tag, p, "expr"); + walk(n.Body, p, "stmt"); + case *ast.TypeCaseClause: + walk(n.Types, p, "type"); + walk(n.Body, p, "stmt"); + case *ast.TypeSwitchStmt: + walk(n.Init, p, "stmt"); + walk(n.Assign, p, "stmt"); + walk(n.Body, p, "stmt"); + case *ast.CommClause: + walk(n.Lhs, p, "expr"); + walk(n.Rhs, p, "expr"); + walk(n.Body, p, "stmt"); + case *ast.SelectStmt: + walk(n.Body, p, "stmt"); + case *ast.ForStmt: + walk(n.Init, p, "stmt"); + walk(&n.Cond, p, "expr"); + walk(n.Post, p, "stmt"); + walk(n.Body, p, "stmt"); + case *ast.RangeStmt: + walk(&n.Key, p, "expr"); + walk(&n.Value, p, "expr"); + walk(&n.X, p, "expr"); + walk(n.Body, p, "stmt"); + + case *ast.ImportSpec: + case *ast.ValueSpec: + walk(&n.Type, p, "type"); + walk(n.Values, p, "expr"); + case *ast.TypeSpec: + walk(&n.Type, p, "type"); + + case *ast.BadDecl: + case *ast.GenDecl: + walk(n.Specs, p, "spec"); + case *ast.FuncDecl: + if n.Recv != nil { + walk(n.Recv, p, "field"); + } + walk(n.Type, p, "type"); + walk(n.Body, p, "stmt"); + + case *ast.File: + walk(n.Decls, p, "decl"); + + case *ast.Package: + for _, f := range n.Files { + walk(f, p, "file"); + } + + case []ast.Decl: + for _, d := range n { + walk(d, p, context); + } + case []ast.Expr: + for i := range n { + walk(&n[i], p, context); + } + case []*ast.Field: + for _, f := range n { + walk(f, p, context); + } + case []ast.Stmt: + for _, s := range n { + walk(s, p, context); + } + case []ast.Spec: + for _, s := range n { + walk(s, p, context); + } + } +} + diff --git a/src/cmd/cgo/cgo.go b/src/cmd/cgo/cgo.go deleted file mode 100644 index f174b7854f..0000000000 --- a/src/cmd/cgo/cgo.go +++ /dev/null @@ -1,308 +0,0 @@ -// 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 ( - "bufio"; - "container/vector"; - "debug/dwarf"; - "debug/elf"; - "flag"; - "fmt"; - "go/ast"; - "go/doc"; - "go/parser"; - "go/scanner"; - "go/token"; - "io"; - "os"; -) - -// Map of uses of C.xxx. The key is the pointer -// to the use (a pointer so it can be rewritten) -// and the value is the context ("call", "expr", "type"). -type cmap map[*ast.Expr] string - -var noPos token.Position - -func usage() { - fmt.Fprint(os.Stderr, "usage: cgo [options] file.cgo\n"); - flag.PrintDefaults(); -} - -func main() { - flag.Usage = usage; - flag.Parse(); - - args := flag.Args(); - if len(args) != 1 { - flag.Usage(); - } - filename := args[0]; - - prog, err := parser.ParsePkgFile("", filename, parser.ParseComments); - if err != nil { - fatal(err); - } - - // Find the import "C" line and get any extra C preamble. - preamble := ""; - found := false; - for _, d := range prog.Decls { - d, ok := d.(*ast.GenDecl); - if !ok { - continue; - } - for _, s := range d.Specs { - s, ok := s.(*ast.ImportSpec); - if !ok { - continue; - } - if len(s.Path) != 1 || string(s.Path[0].Value) != `"C"` { - continue; - } - found = true; - if s.Name != nil { - error(s.Path[0].Pos(), `cannot rename import "C"`); - } - if s.Doc != nil { - preamble += doc.CommentText(s.Doc) + "\n"; - } - else if len(d.Specs) == 1 && d.Doc != nil { - preamble += doc.CommentText(d.Doc) + "\n"; - } - } - } - if !found { - error(noPos, `cannot find import "C"`); - } - - // Accumulate pointers to uses of C.x. - m := make(cmap); - walk(prog, m, "prog"); - - fmt.Print(preamble); - for p, context := range m { - sel := (*p).(*ast.SelectorExpr); - fmt.Printf("%s: %s as %s\n", sel.Pos(), sel.Sel.Value, context); - } -} - -func walk(x interface{}, m cmap, context string) { - switch n := x.(type) { - case *ast.Expr: - 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.Value == "C" { - m[n] = context; - break; - } - } - walk(*n, m, context); - - // everything else just recurs - default: - error(noPos, "unexpected type %T in walk", x); - panic(); - - case nil: - - // These are ordered and grouped to match ../../pkg/go/ast/ast.go - case *ast.Field: - walk(&n.Type, m, "type"); - case *ast.BadExpr: - case *ast.Ident: - case *ast.Ellipsis: - case *ast.BasicLit: - case *ast.StringList: - case *ast.FuncLit: - walk(n.Type, m, "type"); - walk(n.Body, m, "stmt"); - case *ast.CompositeLit: - walk(&n.Type, m, "type"); - walk(n.Elts, m, "expr"); - case *ast.ParenExpr: - walk(&n.X, m, context); - case *ast.SelectorExpr: - walk(&n.X, m, "selector"); - case *ast.IndexExpr: - walk(&n.X, m, "expr"); - walk(&n.Index, m, "expr"); - if n.End != nil { - walk(&n.End, m, "expr"); - } - case *ast.TypeAssertExpr: - walk(&n.X, m, "expr"); - walk(&n.Type, m, "type"); - case *ast.CallExpr: - walk(&n.Fun, m, "call"); - walk(n.Args, m, "expr"); - case *ast.StarExpr: - walk(&n.X, m, context); - case *ast.UnaryExpr: - walk(&n.X, m, "expr"); - case *ast.BinaryExpr: - walk(&n.X, m, "expr"); - walk(&n.Y, m, "expr"); - case *ast.KeyValueExpr: - walk(&n.Key, m, "expr"); - walk(&n.Value, m, "expr"); - - case *ast.ArrayType: - walk(&n.Len, m, "expr"); - walk(&n.Elt, m, "type"); - case *ast.StructType: - walk(n.Fields, m, "field"); - case *ast.FuncType: - walk(n.Params, m, "field"); - walk(n.Results, m, "field"); - case *ast.InterfaceType: - walk(n.Methods, m, "field"); - case *ast.MapType: - walk(&n.Key, m, "type"); - walk(&n.Value, m, "type"); - case *ast.ChanType: - walk(&n.Value, m, "type"); - - case *ast.BadStmt: - case *ast.DeclStmt: - walk(n.Decl, m, "decl"); - case *ast.EmptyStmt: - case *ast.LabeledStmt: - walk(n.Stmt, m, "stmt"); - case *ast.ExprStmt: - walk(&n.X, m, "expr"); - case *ast.IncDecStmt: - walk(&n.X, m, "expr"); - case *ast.AssignStmt: - walk(n.Lhs, m, "expr"); - walk(n.Rhs, m, "expr"); - case *ast.GoStmt: - walk(&n.Call, m, "expr"); - case *ast.DeferStmt: - walk(&n.Call, m, "expr"); - case *ast.ReturnStmt: - walk(n.Results, m, "expr"); - case *ast.BranchStmt: - case *ast.BlockStmt: - walk(n.List, m, "stmt"); - case *ast.IfStmt: - walk(n.Init, m, "stmt"); - walk(&n.Cond, m, "expr"); - walk(n.Body, m, "stmt"); - walk(n.Else, m, "stmt"); - case *ast.CaseClause: - walk(n.Values, m, "expr"); - walk(n.Body, m, "stmt"); - case *ast.SwitchStmt: - walk(n.Init, m, "stmt"); - walk(&n.Tag, m, "expr"); - walk(n.Body, m, "stmt"); - case *ast.TypeCaseClause: - walk(n.Types, m, "type"); - walk(n.Body, m, "stmt"); - case *ast.TypeSwitchStmt: - walk(n.Init, m, "stmt"); - walk(n.Assign, m, "stmt"); - walk(n.Body, m, "stmt"); - case *ast.CommClause: - walk(n.Lhs, m, "expr"); - walk(n.Rhs, m, "expr"); - walk(n.Body, m, "stmt"); - case *ast.SelectStmt: - walk(n.Body, m, "stmt"); - case *ast.ForStmt: - walk(n.Init, m, "stmt"); - walk(&n.Cond, m, "expr"); - walk(n.Post, m, "stmt"); - walk(n.Body, m, "stmt"); - case *ast.RangeStmt: - walk(&n.Key, m, "expr"); - walk(&n.Value, m, "expr"); - walk(&n.X, m, "expr"); - walk(n.Body, m, "stmt"); - - case *ast.ImportSpec: - case *ast.ValueSpec: - walk(&n.Type, m, "type"); - walk(n.Values, m, "expr"); - case *ast.TypeSpec: - walk(&n.Type, m, "type"); - - case *ast.BadDecl: - case *ast.GenDecl: - walk(n.Specs, m, "spec"); - case *ast.FuncDecl: - if n.Recv != nil { - walk(n.Recv, m, "field"); - } - walk(n.Type, m, "type"); - walk(n.Body, m, "stmt"); - - case *ast.File: - walk(n.Decls, m, "decl"); - - case *ast.Package: - for _, f := range n.Files { - walk(f, m, "file"); - } - - case []ast.Decl: - for _, d := range n { - walk(d, m, context); - } - case []ast.Expr: - for i := range n { - walk(&n[i], m, context); - } - case []*ast.Field: - for _, f := range n { - walk(f, m, context); - } - case []ast.Stmt: - for _, s := range n { - walk(s, m, context); - } - case []ast.Spec: - for _, s := range n { - walk(s, m, context); - } - } -} - -func fatal(err os.Error) { - // 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. - if list, ok := err.(scanner.ErrorList); ok { - for _, e := range list { - fmt.Fprintln(os.Stderr, e); - } - } else { - fmt.Fprintln(os.Stderr, err); - } - os.Exit(2); -} - -var nerrors int - -func error(pos token.Position, msg string, args ...) { - nerrors++; - if pos.IsValid() { - fmt.Fprintf(os.Stderr, "%s: ", pos); - } - fmt.Fprintf(os.Stderr, msg, args); - fmt.Fprintf(os.Stderr, "\n"); -} diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go new file mode 100644 index 0000000000..adf19601f8 --- /dev/null +++ b/src/cmd/cgo/gcc.go @@ -0,0 +1,203 @@ +// 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 Crefs in Prog with C types by parsing gcc debug output. + +package main + +import ( + "debug/dwarf"; + "debug/elf"; + "debug/macho"; + "fmt"; + "os"; + "strconv"; + "strings"; +) + +func (p *Prog) loadDebugInfo() { + // Construct a slice of unique names from p.Crefs. + m := make(map[string]int); + for _, c := range p.Crefs { + m[c.Name] = -1; + } + names := make([]string, 0, len(m)); + for name, _ := range m { + i := len(names); + names = names[0:i+1]; + names[i] = name; + m[name] = i; + } + + // 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: + // x.c:2: warning: useless type name in empty declaration + // If name is a value, gcc will print + // x.c:2: warning: statement with no effect + // If name is undeclared, gcc will print + // x.c: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. + var b strings.Buffer; + b.WriteString(p.Preamble); + b.WriteString("void f(void) {\n"); + b.WriteString("#line 0 \"cgo-test\"\n"); + for _, n := range names { + b.WriteString(n); + b.WriteString(";\n"); + } + b.WriteString("}\n"); + + kind := make(map[string]string); + _, stderr := gccDebug(b.Bytes()); + if stderr == "" { + fatal("gcc produced no output"); + } + for _, line := range strings.Split(stderr, "\n", 0) { + if len(line) < 9 || line[0:9] != "cgo-test:" { + continue; + } + line = line[9:len(line)]; + 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.Index(line, "warning: useless type name in empty declaration") >= 0: + what = "type"; + case strings.Index(line, "warning: statement with no effect") >= 0: + what = "value"; + case strings.Index(line, "undeclared") >= 0: + what = "error"; + } + if old, ok := kind[names[i]]; ok && old != what { + error(noPos, "inconsistent gcc output about C.%s", names[i]); + } + kind[names[i]] = what; + } + for _, n := range names { + if _, ok := kind[n]; !ok { + error(noPos, "could not determine kind of name for C.%s", n); + } + } + + // 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. + b.Reset(); + b.WriteString(p.Preamble); + for i, n := range names { + fmt.Fprintf(&b, "typeof(%s) *__cgo__%d;\n", n, i); + } + d, stderr := gccDebug(b.Bytes()); + if d == nil { + fatal("gcc failed:\n%s\non input:\n%s", stderr, b.Bytes()); + } + + // Scan DWARF info for top-level TagVariable entries with AttrName __cgo__i. + types := make([]dwarf.Type, len(names)); + r := d.Reader(); + for { + e, err := r.Next(); + if err != nil { + fatal("reading DWARF entry: %s", err); + } + if e == nil { + break; + } + if e.Tag != dwarf.TagVariable { + goto Continue; + } + name, _ := e.Val(dwarf.AttrName).(string); + typOff, _ := e.Val(dwarf.AttrType).(dwarf.Offset); + if name == "" || typOff == 0 { + fatal("malformed DWARF TagVariable entry"); + } + if !strings.HasPrefix(name, "__cgo__") { + goto Continue; + } + typ, err := d.Type(typOff); + if err != nil { + fatal("loading DWARF type: %s", err); + } + t, ok := typ.(*dwarf.PtrType); + if !ok || t == nil { + fatal("internal error: %s has non-pointer type", name); + } + i, err := strconv.Atoi(name[7:len(name)]); + if err != nil { + fatal("malformed __cgo__ name: %s", name); + } + types[i] = t.Type; + + Continue: + if e.Tag != dwarf.TagCompileUnit { + r.SkipChildren(); + } + } + + // Apply types to Crefs. + for _, c := range p.Crefs { + i := m[c.Name]; + c.TypeName = kind[c.Name] == "type"; + c.DebugType = types[i]; + } +} + +// gccDebug runs gcc -gdwarf-2 over the C program stdin and +// returns the corresponding DWARF data and any messages +// printed to standard error. +func gccDebug(stdin []byte) (*dwarf.Data, string) { + machine := "-m32"; + if os.Getenv("GOARCH") == "amd64" { + machine = "-m64"; + } + + tmp := "_cgo_.o"; + _, stderr, ok := run(stdin, []string{ + "gcc", + machine, + "-Wall", // many warnings + "-Werror", // warnings are errors + "-o"+tmp, // write object to tmp + "-gdwarf-2", // generate DWARF v2 debugging symbols + "-c", // do not link + "-xc", // input language is C + "-", // read input from standard input + }); + if !ok { + return nil, string(stderr); + } + + // Try to parse f as ELF and Mach-O and hope one works. + var f interface{DWARF() (*dwarf.Data, os.Error)}; + var err os.Error; + if f, err = elf.Open(tmp); err != nil { + if f, err = macho.Open(tmp); err != nil { + fatal("cannot parse gcc output %s as ELF or Mach-O object", tmp); + } + } + + d, err := f.DWARF(); + if err != nil { + fatal("cannot load DWARF debug information from %s: %s", tmp, err); + } + return d, ""; +} + diff --git a/src/cmd/cgo/gmp.go b/src/cmd/cgo/gmp.go index 82e4b98a15..cb1f258997 100644 --- a/src/cmd/cgo/gmp.go +++ b/src/cmd/cgo/gmp.go @@ -364,7 +364,7 @@ func (z *Int) Exp(x, y, m *Int) *Int { y.doinit(); z.doinit(); if m == nil { - C.mpz_pow(&z.i, &x.i, &y.i); + C.mpz_pow_ui(&z.i, &x.i, mpz_get_ui(&y.i)); } else { C.mpz_powm(&z.i, &x.i, &y.i, &m.i); } diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go new file mode 100644 index 0000000000..e336d03a56 --- /dev/null +++ b/src/cmd/cgo/main.go @@ -0,0 +1,46 @@ +// 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 ( + "flag"; + "fmt"; + "os"; + "tabwriter"; +) + +// Cgo; see gmp.go for an overview. + +// TODO(rsc): +// Emit correct line number annotations. +// Make 6g understand the annotations. + +func usage() { + fmt.Fprint(os.Stderr, "usage: cgo file.cgo\n"); + flag.PrintDefaults(); +} + +func main() { + flag.Usage = usage; + flag.Parse(); + + args := flag.Args(); + if len(args) != 1 { + usage(); + os.Exit(2); + } + p := openProg(args[0]); + p.loadDebugInfo(); + + tw := tabwriter.NewWriter(os.Stdout, 1, 1, ' ', 0); + for _, cref := range p.Crefs { + what := "value"; + if cref.TypeName { + what = "type"; + } + fmt.Fprintf(tw, "%s:\t%s %s\tC %s\t%s\n", (*cref.Expr).Pos(), cref.Context, cref.Name, what, cref.DebugType); + } + tw.Flush(); +} diff --git a/src/cmd/cgo/util.go b/src/cmd/cgo/util.go new file mode 100644 index 0000000000..3f2fd01c94 --- /dev/null +++ b/src/cmd/cgo/util.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. + +package main + +import ( + "bytes"; + "exec"; + "fmt"; + "go/token"; + "io"; + "os"; +) + +// A ByteReaderAt implements io.ReadAt using a slice of bytes. +type ByteReaderAt []byte + +func (r ByteReaderAt) ReadAt(p []byte, off int64) (n int, err os.Error) { + if off >= int64(len(r)) || off < 0 { + return 0, os.EOF; + } + return bytes.Copy(p, r[off:len(r)]), nil; +} + +// 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 { + fatal("exec %s: %s", argv[0], err); + } + r0, w0, err := os.Pipe(); + if err != nil { + fatal("%s", err); + } + r1, w1, err := os.Pipe(); + if err != nil { + fatal("%s", err); + } + r2, w2, err := os.Pipe(); + if err != nil { + fatal("%s", err); + } + pid, err := os.ForkExec(cmd, argv, os.Environ(), "", []*os.File{r0, w1, w2}); + if err != nil { + fatal("%s", err); + } + r0.Close(); + w1.Close(); + w2.Close(); + c := make(chan bool); + go func() { + w0.Write(stdin); + w0.Close(); + c <- true; + }(); + var xstdout []byte; // TODO(rsc): delete after 6g can take address of out parameter + go func() { + xstdout, _ = io.ReadAll(r1); + r1.Close(); + c <- true; + }(); + stderr, _ = io.ReadAll(r2); + r2.Close(); + <-c; + <-c; + stdout = xstdout; + + w, err := os.Wait(pid, 0); + if err != nil { + fatal("%s", err); + } + ok = w.Exited() && w.ExitStatus() == 0; + return; +} + +// Die with an error message. +func fatal(msg string, args ...) { + fmt.Fprintf(os.Stderr, msg+"\n", args); + os.Exit(2); +} + +var nerrors int +var noPos token.Position + +func error(pos token.Position, msg string, args ...) { + nerrors++; + if pos.IsValid() { + fmt.Fprintf(os.Stderr, "%s: ", pos); + } + fmt.Fprintf(os.Stderr, msg, args); + fmt.Fprintf(os.Stderr, "\n"); +} + -- 2.48.1