1
0
mirror of https://github.com/golang/go synced 2024-11-22 06:54:39 -07:00

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
This commit is contained in:
Russ Cox 2011-06-13 14:43:54 -04:00
parent 8834bb0bfa
commit 9968090ddd
3 changed files with 80 additions and 19 deletions

View File

@ -13,6 +13,7 @@ import (
"debug/elf" "debug/elf"
"debug/macho" "debug/macho"
"debug/pe" "debug/pe"
"encoding/binary"
"flag" "flag"
"fmt" "fmt"
"go/ast" "go/ast"
@ -477,7 +478,27 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
fmt.Fprintf(&b, "enum { __cgo_enum__%d = %s };\n", i, n.C) 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. // Scan DWARF info for top-level TagVariable entries with AttrName __cgo__i.
types := make([]dwarf.Type, len(names)) types := make([]dwarf.Type, len(names))
@ -569,9 +590,12 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
// Remove injected enum to ensure the value will deep-compare // Remove injected enum to ensure the value will deep-compare
// equally in future loads of the same constant. // equally in future loads of the same constant.
n.Type.EnumValues[k] = 0, false 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 // rewriteRef rewrites all the C.xxx references in f.AST to refer to the
@ -593,6 +617,9 @@ func (p *Package) rewriteRef(f *File) {
// are trying to do a ,err call. Also check that // are trying to do a ,err call. Also check that
// functions are only used in calls. // functions are only used in calls.
for _, r := range f.Ref { 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 var expr ast.Expr = ast.NewIdent(r.Name.Mangle) // default
switch r.Context { switch r.Context {
case "call", "call2": case "call", "call2":
@ -692,29 +719,57 @@ func (p *Package) gccCmd() []string {
} }
// gccDebug runs gcc -gdwarf-2 over the C program stdin and // gccDebug runs gcc -gdwarf-2 over the C program stdin and
// returns the corresponding DWARF data and any messages // returns the corresponding DWARF data and, if present, debug data block.
// printed to standard error. func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) {
func (p *Package) gccDebug(stdin []byte) *dwarf.Data {
runGcc(stdin, p.gccCmd()) runGcc(stdin, p.gccCmd())
// Try to parse f as ELF and Mach-O and hope one works. if f, err := macho.Open(gccTmp); err == nil {
var f interface { d, err := f.DWARF()
DWARF() (*dwarf.Data, os.Error) if err != nil {
} fatalf("cannot load DWARF output from %s: %v", gccTmp, err)
var err os.Error }
if f, err = elf.Open(gccTmp); err != nil { var data []byte
if f, err = macho.Open(gccTmp); err != nil { if f.Symtab != nil {
if f, err = pe.Open(gccTmp); err != nil { for i := range f.Symtab.Syms {
fatalf("cannot parse gcc output %s as ELF or Mach-O or PE object", gccTmp) 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() // Can skip debug data block in ELF and PE for now.
if err != nil { // The DWARF information is complete.
fatalf("cannot load DWARF debug information from %s: %s", gccTmp, err)
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 // gccDefines runs gcc -E -dM -xc - over the C program stdin

View File

@ -352,8 +352,8 @@ func (d *Data) Type(off Offset) (Type, os.Error) {
} }
} }
if ndim == 0 { if ndim == 0 {
err = DecodeError{"info", e.Offset, "missing dimension for array"} // LLVM generates this for x[].
goto Error t.Count = -1
} }
case TagBaseType: case TagBaseType:

View File

@ -546,6 +546,12 @@ func (f *File) DWARF() (*dwarf.Data, os.Error) {
return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str) 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 { type ImportedSymbol struct {
Name string Name string
Version string Version string