[release-branch.r58] cgo: handle new Apple LLVM-based gcc from Xcode 4.2

««« CL 4607045 / 142f0bc0d6e7
cgo: handle new Apple LLVM-based gcc from Xcode 4.2

That gcc does not include enumerator names and values
in its DWARF debug output.  Create a data block from which
we can read the values instead.

Fixes #1881.

R=iant
CC=golang-dev
https://golang.org/cl/4607045
»»»

R=adg
CC=golang-dev
https://golang.org/cl/4708042
diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go
index 10411e9..e4e56d8 100644
--- a/src/cmd/cgo/gcc.go
+++ b/src/cmd/cgo/gcc.go
@@ -13,6 +13,7 @@
 	"debug/elf"
 	"debug/macho"
 	"debug/pe"
+	"encoding/binary"
 	"flag"
 	"fmt"
 	"go/ast"
@@ -477,7 +478,27 @@
 			fmt.Fprintf(&b, "enum { __cgo_enum__%d = %s };\n", i, n.C)
 		}
 	}
-	d := p.gccDebug(b.Bytes())
+
+	// 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))
@@ -569,9 +590,12 @@
 				// 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
@@ -593,6 +617,9 @@
 	// 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":
@@ -692,29 +719,57 @@
 }
 
 // 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 *Package) gccDebug(stdin []byte) *dwarf.Data {
+// 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())
 
-	// 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(gccTmp); err != nil {
-		if f, err = macho.Open(gccTmp); err != nil {
-			if f, err = pe.Open(gccTmp); err != nil {
-				fatalf("cannot parse gcc output %s as ELF or Mach-O or PE object", gccTmp)
+	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
 	}
 
-	d, err := f.DWARF()
-	if err != nil {
-		fatalf("cannot load DWARF debug information from %s: %s", gccTmp, err)
+	// 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
 	}
-	return d
+
+	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
diff --git a/src/pkg/debug/dwarf/type.go b/src/pkg/debug/dwarf/type.go
index f9acf11..a33785b 100644
--- a/src/pkg/debug/dwarf/type.go
+++ b/src/pkg/debug/dwarf/type.go
@@ -352,8 +352,8 @@
 			}
 		}
 		if ndim == 0 {
-			err = DecodeError{"info", e.Offset, "missing dimension for array"}
-			goto Error
+			// LLVM generates this for x[].
+			t.Count = -1
 		}
 
 	case TagBaseType:
diff --git a/src/pkg/debug/elf/file.go b/src/pkg/debug/elf/file.go
index 9ae8b41..220ab94 100644
--- a/src/pkg/debug/elf/file.go
+++ b/src/pkg/debug/elf/file.go
@@ -546,6 +546,12 @@
 	return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str)
 }
 
+// Symbols returns the symbol table for f.
+func (f *File) Symbols() ([]Symbol, os.Error) {
+	sym, _, err := f.getSymbols(SHT_SYMTAB)
+	return sym, err
+}
+
 type ImportedSymbol struct {
 	Name    string
 	Version string