[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