go/src/cmd/cgo/gcc.go
Adam Langley 72ec930fa7 Fix cgo for GCC 4.4
Firstly, with -Werror, GCC switched to printing warnings starting
with "error:". Widening the string matches solves this as the messages
are otherwise unchanged.

Secondly, GCC 4.4 outputs DWARF sections with with NUL bytes in all
the offsets and requires the relocation section for .debug_info to be
processed in order to result in valid DWARF data. Thus we add minimal
handling for relocation sections, which is sufficient for our needs.

BUG=1
Fixes #1.

R=rsc, iant
CC=go-dev
http://go/go-review/1017003
2009-11-02 12:02:16 -08:00

639 lines
15 KiB
Go

// 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.
// Conversion of debug output to Go types.
package main
import (
"bytes";
"debug/dwarf";
"debug/elf";
"debug/macho";
"fmt";
"go/ast";
"go/token";
"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 bytes.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 := p.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, ": useless type name in empty declaration") >= 0:
what = "type";
case strings.Index(line, ": 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 := p.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();
}
}
// Record types and typedef information in Crefs.
var conv typeConv;
conv.Init(p.PtrSize);
for _, c := range p.Crefs {
i := m[c.Name];
c.TypeName = kind[c.Name] == "type";
f, fok := types[i].(*dwarf.FuncType);
if c.Context == "call" && !c.TypeName && fok {
c.FuncType = conv.FuncType(f);
} else {
c.Type = conv.Type(types[i]);
}
}
p.Typedef = conv.typedef;
}
func concat(a, b []string) []string {
c := make([]string, len(a)+len(b));
for i, s := range a {
c[i] = s;
}
for i, s := range b {
c[i+len(a)] = s;
}
return c;
}
// gccDebug runs gcc -gdwarf-2 over the C program stdin and
// returns the corresponding DWARF data and any messages
// printed to standard error.
func (p *Prog) gccDebug(stdin []byte) (*dwarf.Data, string) {
machine := "-m32";
if p.PtrSize == 8 {
machine = "-m64";
}
tmp := "_cgo_.o";
base := []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
};
_, stderr, ok := run(stdin, concat(base, p.GccOptions));
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, "";
}
// 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.
byte ast.Expr; // denotes padding
int8, int16, int32, int64 ast.Expr;
uint8, uint16, uint32, uint64, uintptr ast.Expr;
float32, float64 ast.Expr;
void ast.Expr;
unsafePointer ast.Expr;
string ast.Expr;
ptrSize int64;
tagGen int;
}
func (c *typeConv) Init(ptrSize int64) {
c.ptrSize = ptrSize;
c.m = make(map[dwarf.Type]*Type);
c.typedef = make(map[string]ast.Expr);
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.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 cnameMap = 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",
};
// 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 {
fatal("type conversion loop at %s", dtype);
}
return t;
}
t := new(Type);
t.Size = dtype.Size();
t.Align = -1;
t.C = dtype.Common().Name;
if t.Size < 0 {
fatal("dwarf.Type %s reports unknown size", dtype);
}
c.m[dtype] = t;
switch dt := dtype.(type) {
default:
fatal("unexpected type: %s", dtype);
case *dwarf.AddrType:
if t.Size != c.ptrSize {
fatal("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 = fmt.Sprintf("typeof(%s[%d])", sub.C, dt.Count);
case *dwarf.CharType:
if t.Size != 1 {
fatal("unexpected: %d-byte char type - %s", t.Size, dtype);
}
t.Go = c.int8;
t.Align = 1;
case *dwarf.EnumType:
switch t.Size {
default:
fatal("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;
}
if t.Align = t.Size; t.Align >= c.ptrSize {
t.Align = c.ptrSize;
}
t.C = "enum " + dt.EnumName;
case *dwarf.FloatType:
switch t.Size {
default:
fatal("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.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 {
fatal("unexpected: %d-bit int type - %s", dt.BitSize, dtype);
}
switch t.Size {
default:
fatal("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 = "void*";
break;
}
gt := &ast.StarExpr{};
t.Go = gt; // publish before recursive call
sub := c.Type(dt.Type);
gt.X = sub.Go;
t.C = 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(c.tagGen);
c.tagGen++;
} else if t.C == "" {
t.C = dt.Kind + " " + tag;
}
name := c.Ident("_C" + dt.Kind + "_" + tag);
t.Go = name; // publish before recursive calls
switch dt.Kind {
case "union", "class":
c.typedef[name.Value] = c.Opaque(t.Size);
if t.C == "" {
t.C = fmt.Sprintf("typeof(unsigned char[%d])", t.Size);
}
case "struct":
g, csyntax, align := c.Struct(dt);
if t.C == "" {
t.C = csyntax;
}
t.Align = align;
c.typedef[name.Value] = 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;
}
name := c.Ident("_C_" + dt.Name);
t.Go = name; // publish before recursive call
sub := c.Type(dt.Type);
t.Size = sub.Size;
t.Align = sub.Align;
if _, ok := c.typedef[name.Value]; !ok {
c.typedef[name.Value] = sub.Go;
}
case *dwarf.UcharType:
if t.Size != 1 {
fatal("unexpected: %d-byte uchar type - %s", t.Size, dtype);
}
t.Go = c.uint8;
t.Align = 1;
case *dwarf.UintType:
if dt.BitSize > 0 {
fatal("unexpected: %d-bit uint type - %s", dt.BitSize, dtype);
}
switch t.Size {
default:
fatal("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 = "void";
}
switch dtype.(type) {
case *dwarf.AddrType, *dwarf.CharType, *dwarf.IntType, *dwarf.FloatType, *dwarf.UcharType, *dwarf.UintType:
s := dtype.Common().Name;
if s != "" {
if ss, ok := cnameMap[s]; ok {
s = ss;
}
s = strings.Join(strings.Split(s, " ", 0), ""); // strip spaces
name := c.Ident("_C_" + s);
c.typedef[name.Value] = t.Go;
t.Go = name;
}
}
if t.C == "" {
fatal("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.
return &Type{
Size: c.ptrSize,
Align: c.ptrSize,
Go: &ast.StarExpr{X: t.Go},
C: t.C + "*"
};
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 {
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 {
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: gp,
Results: gr
}
};
}
// Identifier
func (c *typeConv) Ident(s string) *ast.Ident {
return &ast.Ident{Value: 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: strings.Bytes(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
func (c *typeConv) Struct(dt *dwarf.StructType) (expr *ast.StructType, csyntax string, align int64) {
csyntax = "struct { ";
fld := make([]*ast.Field, 0, 2*len(dt.Field)+1); // enough for padding around every field
off := int64(0);
for _, f := range dt.Field {
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(f.Name)}, Type: t.Go};
off += t.Size;
csyntax += t.C + " " + f.Name + "; ";
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 {
fatal("struct size calculation error");
}
csyntax += "}";
expr = &ast.StructType{Fields: fld};
return;
}