summaryrefslogtreecommitdiff
path: root/src/cmd/cgo/gcc.go
diff options
context:
space:
mode:
authorOndřej Surý <ondrej@sury.org>2012-01-30 15:38:19 +0100
committerOndřej Surý <ondrej@sury.org>2012-01-30 15:38:19 +0100
commit4cecda6c347bd6902b960c6a35a967add7070b0d (patch)
treea462e224ff41ec9f3eb1a0b6e815806f9e8804ad /src/cmd/cgo/gcc.go
parent6c7ca6e4d4e26e4c8cbe0d183966011b3b088a0a (diff)
downloadgolang-4cecda6c347bd6902b960c6a35a967add7070b0d.tar.gz
Imported Upstream version 2012.01.27upstream-weekly/2012.01.27
Diffstat (limited to 'src/cmd/cgo/gcc.go')
-rw-r--r--src/cmd/cgo/gcc.go284
1 files changed, 222 insertions, 62 deletions
diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go
index 04d95f0b9..155eb0440 100644
--- a/src/cmd/cgo/gcc.go
+++ b/src/cmd/cgo/gcc.go
@@ -14,6 +14,7 @@ import (
"debug/macho"
"debug/pe"
"encoding/binary"
+ "errors"
"flag"
"fmt"
"go/ast"
@@ -23,6 +24,7 @@ import (
"strconv"
"strings"
"unicode"
+ "unicode/utf8"
)
var debugDefine = flag.Bool("debug-define", false, "print relevant #defines")
@@ -58,6 +60,9 @@ func cname(s string) string {
if strings.HasPrefix(s, "enum_") {
return "enum " + s[len("enum_"):]
}
+ if strings.HasPrefix(s, "sizeof_") {
+ return "sizeof(" + cname(s[len("sizeof_"):]) + ")"
+ }
return s
}
@@ -71,7 +76,7 @@ func (p *Package) ParseFlags(f *File, srcfile string) {
NextLine:
for _, line := range linesIn {
l := strings.TrimSpace(line)
- if len(l) < 5 || l[:4] != "#cgo" || !unicode.IsSpace(int(l[4])) {
+ if len(l) < 5 || l[:4] != "#cgo" || !unicode.IsSpace(rune(l[4])) {
linesOut = append(linesOut, line)
continue
}
@@ -147,10 +152,10 @@ func (p *Package) addToFlag(flag string, args []string) {
// pkgConfig runs pkg-config and extracts --libs and --cflags information
// for packages.
-func pkgConfig(packages []string) (cflags, ldflags []string, err os.Error) {
+func pkgConfig(packages []string) (cflags, ldflags []string, err error) {
for _, name := range packages {
if len(name) == 0 || name[0] == '-' {
- return nil, nil, os.NewError(fmt.Sprintf("invalid name: %q", name))
+ return nil, nil, errors.New(fmt.Sprintf("invalid name: %q", name))
}
}
@@ -158,7 +163,7 @@ func pkgConfig(packages []string) (cflags, ldflags []string, err os.Error) {
stdout, stderr, ok := run(nil, args)
if !ok {
os.Stderr.Write(stderr)
- return nil, nil, os.NewError("pkg-config failed")
+ return nil, nil, errors.New("pkg-config failed")
}
cflags, err = splitQuoted(string(stdout))
if err != nil {
@@ -169,7 +174,7 @@ func pkgConfig(packages []string) (cflags, ldflags []string, err os.Error) {
stdout, stderr, ok = run(nil, args)
if !ok {
os.Stderr.Write(stderr)
- return nil, nil, os.NewError("pkg-config failed")
+ return nil, nil, errors.New("pkg-config failed")
}
ldflags, err = splitQuoted(string(stdout))
return
@@ -191,30 +196,30 @@ func pkgConfig(packages []string) (cflags, ldflags []string, err os.Error) {
//
// []string{"a", "b:c d", "ef", `g"`}
//
-func splitQuoted(s string) (r []string, err os.Error) {
+func splitQuoted(s string) (r []string, err error) {
var args []string
- arg := make([]int, len(s))
+ arg := make([]rune, len(s))
escaped := false
quoted := false
- quote := 0
+ quote := '\x00'
i := 0
- for _, rune := range s {
+ for _, r := range s {
switch {
case escaped:
escaped = false
- case rune == '\\':
+ case r == '\\':
escaped = true
continue
case quote != 0:
- if rune == quote {
+ if r == quote {
quote = 0
continue
}
- case rune == '"' || rune == '\'':
+ case r == '"' || r == '\'':
quoted = true
- quote = rune
+ quote = r
continue
- case unicode.IsSpace(rune):
+ case unicode.IsSpace(r):
if quoted || i > 0 {
quoted = false
args = append(args, string(arg[:i]))
@@ -222,21 +227,21 @@ func splitQuoted(s string) (r []string, err os.Error) {
}
continue
}
- arg[i] = rune
+ arg[i] = r
i++
}
if quoted || i > 0 {
args = append(args, string(arg[:i]))
}
if quote != 0 {
- err = os.NewError("unclosed quote")
+ err = errors.New("unclosed quote")
} else if escaped {
- err = os.NewError("unfinished escaping")
+ err = errors.New("unfinished escaping")
}
return args, err
}
-var safeBytes = []byte("+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz")
+var safeBytes = []byte(`+-.,/0123456789:=ABCDEFGHIJKLMNOPQRSTUVWXYZ\_abcdefghijklmnopqrstuvwxyz`)
func safeName(s string) bool {
if s == "" {
@@ -339,14 +344,22 @@ func (p *Package) guessKinds(f *File) []*Name {
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 {
+ if _, err := parser.ParseExpr(n.Define); err == nil {
ok = true
}
}
if ok {
n.Kind = "const"
- n.Const = n.Define
+ // Turn decimal into hex, just for consistency
+ // with enum-derived constants. Otherwise
+ // in the cgo -godefs output half the constants
+ // are in hex and half are in whatever the #define used.
+ i, err := strconv.ParseInt(n.Define, 0, 64)
+ if err == nil {
+ n.Const = fmt.Sprintf("%#x", i)
+ } else {
+ n.Const = n.Define
+ }
continue
}
@@ -420,7 +433,7 @@ func (p *Package) guessKinds(f *File) []*Name {
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:]))
+ error_(token.NoPos, "%s", strings.TrimSpace(line[colon+1:]))
case strings.Contains(line, "is not an integer constant"):
isConst[i] = false
continue
@@ -439,6 +452,11 @@ func (p *Package) guessKinds(f *File) []*Name {
for i, b := range isConst {
if b {
names[i].Kind = "const"
+ if toSniff[i] != nil && names[i].Const == "" {
+ j := len(needType)
+ needType = needType[0 : j+1]
+ needType[j] = names[i]
+ }
}
}
for _, n := range toSniff {
@@ -448,7 +466,7 @@ func (p *Package) guessKinds(f *File) []*Name {
if n.Kind != "" {
continue
}
- error(token.NoPos, "could not determine kind of name for C.%s", n.Go)
+ error_(token.NoPos, "could not determine kind of name for C.%s", n.Go)
}
if nerrors > 0 {
fatalf("unresolved names")
@@ -576,6 +594,9 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
var conv typeConv
conv.Init(p.PtrSize)
for i, n := range names {
+ if types[i] == nil {
+ continue
+ }
f, fok := types[i].(*dwarf.FuncType)
if n.Kind != "type" && fok {
n.Kind = "func"
@@ -585,12 +606,12 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
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])
+ n.Const = fmt.Sprintf("%#x", 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
+ delete(n.Type.EnumValues, k)
} else if n.Kind == "const" && i < len(enumVal) {
- n.Const = strconv.Itoa64(enumVal[i])
+ n.Const = fmt.Sprintf("%#x", enumVal[i])
}
}
}
@@ -599,7 +620,8 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
// 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.
+// the xxx. In *godefs or *cdefs mode, rewriteRef replaces the names
+// with full definitions instead of mangled names.
func (p *Package) rewriteRef(f *File) {
// Assign mangled names.
for _, n := range f.Name {
@@ -617,7 +639,7 @@ func (p *Package) rewriteRef(f *File) {
// 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)
+ 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 {
@@ -628,12 +650,12 @@ func (p *Package) rewriteRef(f *File) {
expr = r.Name.Type.Go
break
}
- error(r.Pos(), "call of non-function C.%s", r.Name.Go)
+ 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")
+ error_(r.Pos(), "assignment count mismatch: 2 = 0")
}
// Invent new Name for the two-result function.
n := f.Name["2"+r.Name.Go]
@@ -650,7 +672,7 @@ func (p *Package) rewriteRef(f *File) {
}
case "expr":
if r.Name.Kind == "func" {
- error(r.Pos(), "must call C.%s", r.Name.Go)
+ error_(r.Pos(), "must call C.%s", r.Name.Go)
}
if r.Name.Kind == "type" {
// Okay - might be new(T)
@@ -662,13 +684,28 @@ func (p *Package) rewriteRef(f *File) {
case "type":
if r.Name.Kind != "type" {
- error(r.Pos(), "expression C.%s used as type", r.Name.Go)
+ error_(r.Pos(), "expression C.%s used as type", r.Name.Go)
+ } else if r.Name.Type == nil {
+ // Use of C.enum_x, C.struct_x or C.union_x without C definition.
+ // GCC won't raise an error when using pointers to such unknown types.
+ error_(r.Pos(), "type C.%s: undefined C type '%s'", r.Name.Go, r.Name.C)
} else {
expr = r.Name.Type.Go
}
default:
if r.Name.Kind == "func" {
- error(r.Pos(), "must call C.%s", r.Name.Go)
+ error_(r.Pos(), "must call C.%s", r.Name.Go)
+ }
+ }
+ if *godefs || *cdefs {
+ // Substitute definition for mangled type name.
+ if id, ok := expr.(*ast.Ident); ok {
+ if t := typedef[id.Name]; t != nil {
+ expr = t
+ }
+ if id.Name == r.Name.Mangle && r.Name.Const != "" {
+ expr = ast.NewIdent(r.Name.Const)
+ }
}
}
*r.Expr = expr
@@ -696,7 +733,9 @@ func (p *Package) gccMachine() []string {
return nil
}
-var gccTmp = objDir + "_cgo_.o"
+func gccTmp() string {
+ return *objDir + "_cgo_.o"
+}
// gccCmd returns the gcc command line to use for compiling
// the input.
@@ -705,7 +744,7 @@ func (p *Package) gccCmd() []string {
p.gccName(),
"-Wall", // many warnings
"-Werror", // warnings are errors
- "-o" + gccTmp, // write object to tmp
+ "-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
@@ -722,10 +761,10 @@ func (p *Package) gccCmd() []string {
func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) {
runGcc(stdin, p.gccCmd())
- if f, err := macho.Open(gccTmp); err == nil {
+ 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)
+ fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)
}
var data []byte
if f.Symtab != nil {
@@ -751,23 +790,23 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte)
// Can skip debug data block in ELF and PE for now.
// The DWARF information is complete.
- if f, err := elf.Open(gccTmp); err == nil {
+ 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)
+ fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)
}
return d, f.ByteOrder, nil
}
- if f, err := pe.Open(gccTmp); err == 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)
+ 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)
+ fatalf("cannot parse gcc output %s as ELF, Mach-O, PE object", gccTmp())
panic("not reached")
}
@@ -848,6 +887,7 @@ type typeConv struct {
var tagGen int
var typedef = make(map[string]ast.Expr)
+var goIdent = make(map[string]*ast.Ident)
func (c *typeConv) Init(ptrSize int64) {
c.ptrSize = ptrSize
@@ -1113,6 +1153,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type {
}
name := c.Ident("_Ctype_" + dt.Kind + "_" + tag)
t.Go = name // publish before recursive calls
+ goIdent[name.Name] = name
switch dt.Kind {
case "union", "class":
typedef[name.Name] = c.Opaque(t.Size)
@@ -1147,7 +1188,8 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type {
t.Align = c.ptrSize
break
}
- name := c.Ident("_Ctypedef_" + dt.Name)
+ name := c.Ident("_Ctype_" + dt.Name)
+ goIdent[name.Name] = name
t.Go = name // publish before recursive call
sub := c.Type(dt.Type)
t.Size = sub.Size
@@ -1155,6 +1197,9 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type {
if _, ok := typedef[name.Name]; !ok {
typedef[name.Name] = sub.Go
}
+ if *godefs || *cdefs {
+ t.Go = sub.Go
+ }
case *dwarf.UcharType:
if t.Size != 1 {
@@ -1198,7 +1243,9 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type {
s = strings.Join(strings.Split(s, " "), "") // strip spaces
name := c.Ident("_Ctype_" + s)
typedef[name.Name] = t.Go
- t.Go = name
+ if !*godefs && !*cdefs {
+ t.Go = name
+ }
}
}
@@ -1263,7 +1310,7 @@ func (c *typeConv) FuncType(dtype *dwarf.FuncType) *FuncType {
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}}
+ gr = []*ast.Field{{Type: r.Go}}
}
return &FuncType{
Params: p,
@@ -1292,7 +1339,7 @@ func (c *typeConv) Opaque(n int64) ast.Expr {
func (c *typeConv) intExpr(n int64) ast.Expr {
return &ast.BasicLit{
Kind: token.INT,
- Value: strconv.Itoa64(n),
+ Value: strconv.FormatInt(n, 10),
}
}
@@ -1323,38 +1370,61 @@ func (c *typeConv) Struct(dt *dwarf.StructType) (expr *ast.StructType, csyntax s
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] {
+ if !*godefs && !*cdefs {
+ for cid, goid := range ident {
+ if token.Lookup(goid).IsKeyword() {
+ // Avoid keyword
goid = "_" + goid
- }
- used[goid] = true
- ident[cid] = goid
+ // Also avoid existing fields
+ for _, exist := used[goid]; exist; _, exist = used[goid] {
+ goid = "_" + goid
+ }
+
+ used[goid] = true
+ ident[cid] = goid
+ }
}
}
+ anon := 0
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)
+ tgo := t.Go
+ size := t.Size
+
+ if f.BitSize > 0 {
+ if f.BitSize%8 != 0 {
+ continue
+ }
+ size = f.BitSize / 8
+ name := tgo.(*ast.Ident).String()
+ if strings.HasPrefix(name, "int") {
+ name = "int"
+ } else {
+ name = "uint"
+ }
+ tgo = ast.NewIdent(name + fmt.Sprint(f.BitSize))
+ }
+
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
+ name := f.Name
+ if name == "" {
+ name = fmt.Sprintf("anon%d", anon)
+ anon++
+ ident[name] = name
+ }
+ fld[n] = &ast.Field{Names: []*ast.Ident{c.Ident(ident[name])}, Type: tgo}
+ off += size
buf.WriteString(t.C.String())
buf.WriteString(" ")
- buf.WriteString(f.Name)
+ buf.WriteString(name)
buf.WriteString("; ")
if t.Align > align {
align = t.Align
@@ -1369,6 +1439,96 @@ func (c *typeConv) Struct(dt *dwarf.StructType) (expr *ast.StructType, csyntax s
}
buf.WriteString("}")
csyntax = buf.String()
+
+ if *godefs || *cdefs {
+ godefsFields(fld)
+ }
expr = &ast.StructType{Fields: &ast.FieldList{List: fld}}
return
}
+
+func upper(s string) string {
+ if s == "" {
+ return ""
+ }
+ r, size := utf8.DecodeRuneInString(s)
+ if r == '_' {
+ return "X" + s
+ }
+ return string(unicode.ToUpper(r)) + s[size:]
+}
+
+// godefsFields rewrites field names for use in Go or C definitions.
+// It strips leading common prefixes (like tv_ in tv_sec, tv_usec)
+// converts names to upper case, and rewrites _ into Pad_godefs_n,
+// so that all fields are exported.
+func godefsFields(fld []*ast.Field) {
+ prefix := fieldPrefix(fld)
+ npad := 0
+ for _, f := range fld {
+ for _, n := range f.Names {
+ if strings.HasPrefix(n.Name, prefix) && n.Name != prefix {
+ n.Name = n.Name[len(prefix):]
+ }
+ if n.Name == "_" {
+ // Use exported name instead.
+ n.Name = "Pad_cgo_" + strconv.Itoa(npad)
+ npad++
+ }
+ if !*cdefs {
+ n.Name = upper(n.Name)
+ }
+ }
+ p := &f.Type
+ t := *p
+ if star, ok := t.(*ast.StarExpr); ok {
+ star = &ast.StarExpr{X: star.X}
+ *p = star
+ p = &star.X
+ t = *p
+ }
+ if id, ok := t.(*ast.Ident); ok {
+ if id.Name == "unsafe.Pointer" {
+ *p = ast.NewIdent("*byte")
+ }
+ }
+ }
+}
+
+// fieldPrefix returns the prefix that should be removed from all the
+// field names when generating the C or Go code. For generated
+// C, we leave the names as is (tv_sec, tv_usec), since that's what
+// people are used to seeing in C. For generated Go code, such as
+// package syscall's data structures, we drop a common prefix
+// (so sec, usec, which will get turned into Sec, Usec for exporting).
+func fieldPrefix(fld []*ast.Field) string {
+ if *cdefs {
+ return ""
+ }
+ prefix := ""
+ for _, f := range fld {
+ for _, n := range f.Names {
+ // Ignore field names that don't have the prefix we're
+ // looking for. It is common in C headers to have fields
+ // named, say, _pad in an otherwise prefixed header.
+ // If the struct has 3 fields tv_sec, tv_usec, _pad1, then we
+ // still want to remove the tv_ prefix.
+ // The check for "orig_" here handles orig_eax in the
+ // x86 ptrace register sets, which otherwise have all fields
+ // with reg_ prefixes.
+ if strings.HasPrefix(n.Name, "orig_") || strings.HasPrefix(n.Name, "_") {
+ continue
+ }
+ i := strings.Index(n.Name, "_")
+ if i < 0 {
+ continue
+ }
+ if prefix == "" {
+ prefix = n.Name[:i+1]
+ } else if prefix != n.Name[:i+1] {
+ return ""
+ }
+ }
+ }
+ return prefix
+}