Skip to content
Snippets Groups Projects
gcc.go 34.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • // 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.
    
    
    Russ Cox's avatar
    Russ Cox committed
    // Annotate Ref in Prog with C types by parsing gcc debug output.
    
    Russ Cox's avatar
    Russ Cox committed
    // Conversion of debug output to Go types.
    
    	"bytes"
    	"debug/dwarf"
    	"debug/elf"
    	"debug/macho"
    
    	"debug/pe"
    
    Russ Cox's avatar
    Russ Cox committed
    	"flag"
    
    Russ Cox's avatar
    Russ Cox committed
    var debugDefine = flag.Bool("debug-define", false, "print relevant #defines")
    var debugGcc = flag.Bool("debug-gcc", false, "print gcc invocations")
    
    var nameToC = map[string]string{
    
    	"schar":         "signed char",
    	"uchar":         "unsigned char",
    	"ushort":        "unsigned short",
    	"uint":          "unsigned int",
    	"ulong":         "unsigned long",
    	"longlong":      "long long",
    	"ulonglong":     "unsigned long long",
    	"complexfloat":  "float complex",
    	"complexdouble": "double complex",
    
    Russ Cox's avatar
    Russ Cox committed
    }
    
    // cname returns the C name to use for C.s.
    // The expansions are listed in nameToC and also
    // struct_foo becomes "struct foo", and similarly for
    // union and enum.
    func cname(s string) string {
    	if t, ok := nameToC[s]; ok {
    		return t
    	}
    
    	if strings.HasPrefix(s, "struct_") {
    		return "struct " + s[len("struct_"):]
    	}
    	if strings.HasPrefix(s, "union_") {
    		return "union " + s[len("union_"):]
    	}
    	if strings.HasPrefix(s, "enum_") {
    		return "enum " + s[len("enum_"):]
    	}
    	return s
    }
    
    
    // ParseFlags extracts #cgo CFLAGS and LDFLAGS options from the file
    // preamble. Multiple occurrences are concatenated with a separating space,
    // even across files.
    func (p *Package) ParseFlags(f *File, srcfile string) {
    	linesIn := strings.Split(f.Preamble, "\n", -1)
    	linesOut := make([]string, 0, len(linesIn))
    
    	for _, line := range linesIn {
    		l := strings.TrimSpace(line)
    		if len(l) < 5 || l[:4] != "#cgo" || !unicode.IsSpace(int(l[4])) {
    			linesOut = append(linesOut, line)
    			continue
    		}
    
    		l = strings.TrimSpace(l[4:])
    		fields := strings.Split(l, ":", 2)
    		if len(fields) != 2 {
    
    			fatalf("%s: bad #cgo line: %s", srcfile, line)
    
    		var k string
    		kf := strings.Fields(fields[0])
    		switch len(kf) {
    		case 1:
    			k = kf[0]
    		case 2:
    			k = kf[1]
    			switch kf[0] {
    			case runtime.GOOS:
    			case runtime.GOARCH:
    			case runtime.GOOS + "/" + runtime.GOARCH:
    			default:
    				continue NextLine
    			}
    		default:
    
    			fatalf("%s: bad #cgo option: %s", srcfile, fields[0])
    
    		args, err := splitQuoted(fields[1])
    
    			fatalf("%s: bad #cgo option %s: %s", srcfile, k, err)
    
    		for _, arg := range args {
    			if !safeName(arg) {
    				fatalf("%s: #cgo option %s is unsafe: %s", srcfile, k, arg)
    			}
    		}
    
    
    		switch k {
    
    		case "CFLAGS", "LDFLAGS":
    			p.addToFlag(k, args)
    
    		case "pkg-config":
    			cflags, ldflags, err := pkgConfig(args)
    			if err != nil {
    				fatalf("%s: bad #cgo option %s: %s", srcfile, k, err)
    			}
    			p.addToFlag("CFLAGS", cflags)
    			p.addToFlag("LDFLAGS", ldflags)
    
    		default:
    			fatalf("%s: unsupported #cgo option %s", srcfile, k)
    
    
    		}
    	}
    	f.Preamble = strings.Join(linesOut, "\n")
    }
    
    
    // addToFlag appends args to flag.  All flags are later written out onto the
    // _cgo_flags file for the build system to use.
    func (p *Package) addToFlag(flag string, args []string) {
    	if oldv, ok := p.CgoFlags[flag]; ok {
    		p.CgoFlags[flag] = oldv + " " + strings.Join(args, " ")
    	} else {
    		p.CgoFlags[flag] = strings.Join(args, " ")
    	}
    	if flag == "CFLAGS" {
    		// We'll also need these when preprocessing for dwarf information.
    		p.GccOptions = append(p.GccOptions, args...)
    	}
    }
    
    // pkgConfig runs pkg-config and extracts --libs and --cflags information
    // for packages.
    func pkgConfig(packages []string) (cflags, ldflags []string, err os.Error) {
    	for _, name := range packages {
    
    		if len(name) == 0 || name[0] == '-' {
    
    			return nil, nil, os.NewError(fmt.Sprintf("invalid name: %q", name))
    		}
    	}
    
    	args := append([]string{"pkg-config", "--cflags"}, packages...)
    	stdout, stderr, ok := run(nil, args)
    	if !ok {
    		os.Stderr.Write(stderr)
    		return nil, nil, os.NewError("pkg-config failed")
    	}
    	cflags, err = splitQuoted(string(stdout))
    	if err != nil {
    		return
    	}
    
    	args = append([]string{"pkg-config", "--libs"}, packages...)
    	stdout, stderr, ok = run(nil, args)
    	if !ok {
    		os.Stderr.Write(stderr)
    		return nil, nil, os.NewError("pkg-config failed")
    	}
    	ldflags, err = splitQuoted(string(stdout))
    	return
    }
    
    
    // splitQuoted splits the string s around each instance of one or more consecutive
    // white space characters while taking into account quotes and escaping, and
    // returns an array of substrings of s or an empty list if s contains only white space.
    // Single quotes and double quotes are recognized to prevent splitting within the
    // quoted region, and are removed from the resulting substrings. If a quote in s
    // isn't closed err will be set and r will have the unclosed argument as the
    // last element.  The backslash is used for escaping.
    //
    // For example, the following string:
    //
    //     `a b:"c d" 'e''f'  "g\""`
    //
    // Would be parsed as:
    //
    //     []string{"a", "b:c d", "ef", `g"`}
    //
    func splitQuoted(s string) (r []string, err os.Error) {
    	var args []string
    	arg := make([]int, len(s))
    	escaped := false
    	quoted := false
    	quote := 0
    	i := 0
    	for _, rune := range s {
    		switch {
    		case escaped:
    			escaped = false
    		case rune == '\\':
    			escaped = true
    			continue
    		case quote != 0:
    			if rune == quote {
    				quote = 0
    				continue
    			}
    		case rune == '"' || rune == '\'':
    			quoted = true
    			quote = rune
    			continue
    		case unicode.IsSpace(rune):
    			if quoted || i > 0 {
    				quoted = false
    				args = append(args, string(arg[:i]))
    				i = 0
    			}
    			continue
    		}
    		arg[i] = rune
    		i++
    	}
    	if quoted || i > 0 {
    		args = append(args, string(arg[:i]))
    	}
    	if quote != 0 {
    		err = os.ErrorString("unclosed quote")
    	} else if escaped {
    		err = os.ErrorString("unfinished escaping")
    	}
    	return args, err
    }
    
    
    var safeBytes = []byte("+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz")
    
    
    func safeName(s string) bool {
    	if s == "" {
    		return false
    	}
    	for i := 0; i < len(s); i++ {
    		if c := s[i]; c < 0x80 && bytes.IndexByte(safeBytes, c) < 0 {
    			return false
    		}
    	}
    	return true
    }
    
    
    Russ Cox's avatar
    Russ Cox committed
    // Translate rewrites f.AST, the original Go input, to remove
    // references to the imported package C, replacing them with
    // references to the equivalent Go types, functions, and variables.
    func (p *Package) Translate(f *File) {
    	for _, cref := range f.Ref {
    		// Convert C.ulong to C.unsigned long, etc.
    		cref.Name.C = cname(cref.Name.Go)
    	}
    	p.loadDefines(f)
    	needType := p.guessKinds(f)
    	if len(needType) > 0 {
    		p.loadDWARF(f, needType)
    	}
    	p.rewriteRef(f)
    }
    
    // loadDefines coerces gcc into spitting out the #defines in use
    // in the file f and saves relevant renamings in f.Name[name].Define.
    func (p *Package) loadDefines(f *File) {
    
    Russ Cox's avatar
    Russ Cox committed
    	b.WriteString(builtinProlog)
    	b.WriteString(f.Preamble)
    	stdout := p.gccDefines(b.Bytes())
    
    	for _, line := range strings.Split(stdout, "\n", -1) {
    
    		if len(line) < 9 || line[0:7] != "#define" {
    			continue
    		}
    
    		line = strings.TrimSpace(line[8:])
    
    		var key, val string
    		spaceIndex := strings.Index(line, " ")
    		tabIndex := strings.Index(line, "\t")
    
    		if spaceIndex == -1 && tabIndex == -1 {
    			continue
    		} else if tabIndex == -1 || (spaceIndex != -1 && spaceIndex < tabIndex) {
    			key = line[0:spaceIndex]
    			val = strings.TrimSpace(line[spaceIndex:])
    		} else {
    			key = line[0:tabIndex]
    			val = strings.TrimSpace(line[tabIndex:])
    		}
    
    
    Russ Cox's avatar
    Russ Cox committed
    		if n := f.Name[key]; n != nil {
    			if *debugDefine {
    				fmt.Fprintf(os.Stderr, "#define %s %s\n", key, val)
    
    Russ Cox's avatar
    Russ Cox committed
    			n.Define = val
    
    Russ Cox's avatar
    Russ Cox committed
    }
    
    Russ Cox's avatar
    Russ Cox committed
    // guessKinds tricks gcc into revealing the kind of each
    // name xxx for the references C.xxx in the Go input.
    // The kind is either a constant, type, or variable.
    func (p *Package) guessKinds(f *File) []*Name {
    
    	// 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:
    
    Russ Cox's avatar
    Russ Cox committed
    	//	cgo-test:2: warning: useless type name in empty declaration
    
    	// If name is a value, gcc will print
    
    Russ Cox's avatar
    Russ Cox committed
    	//	cgo-test:2: warning: statement with no effect
    
    	// If name is undeclared, gcc will print
    
    Russ Cox's avatar
    Russ Cox committed
    	//	cgo-test: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.
    
    Russ Cox's avatar
    Russ Cox committed
    	//
    	// The line also has an enum declaration:
    	//	name; enum { _cgo_enum_1 = name };
    	// If name is not a constant, gcc will print:
    	//	cgo-test:4: error: enumerator value for '_cgo_enum_4' is not an integer constant
    	// we assume lines without that error are constants.
    
    	// Make list of names that need sniffing, type lookup.
    	toSniff := make([]*Name, 0, len(f.Name))
    	needType := make([]*Name, 0, len(f.Name))
    
    	for _, n := range f.Name {
    		// If we've already found this name as a #define
    		// and we can translate it as a constant value, do so.
    		if n.Define != "" {
    			ok := false
    			if _, err := strconv.Atoi(n.Define); err == nil {
    				ok = true
    			} else if n.Define[0] == '"' || n.Define[0] == '\'' {
    
    				_, err := parser.ParseExpr(fset, "", n.Define)
    
    Russ Cox's avatar
    Russ Cox committed
    				if err == nil {
    					ok = true
    				}
    			}
    			if ok {
    				n.Kind = "const"
    				n.Const = n.Define
    				continue
    			}
    
    			if isName(n.Define) {
    				n.C = n.Define
    			}
    		}
    
    		// If this is a struct, union, or enum type name,
    		// record the kind but also that we need type information.
    		if strings.HasPrefix(n.C, "struct ") || strings.HasPrefix(n.C, "union ") || strings.HasPrefix(n.C, "enum ") {
    			n.Kind = "type"
    			i := len(needType)
    			needType = needType[0 : i+1]
    			needType[i] = n
    			continue
    		}
    
    		i := len(toSniff)
    		toSniff = toSniff[0 : i+1]
    		toSniff[i] = n
    	}
    
    	if len(toSniff) == 0 {
    		return needType
    	}
    
    	var b bytes.Buffer
    	b.WriteString(builtinProlog)
    	b.WriteString(f.Preamble)
    
    	b.WriteString("void __cgo__f__(void) {\n")
    
    	b.WriteString("#line 0 \"cgo-test\"\n")
    
    Russ Cox's avatar
    Russ Cox committed
    	for i, n := range toSniff {
    		fmt.Fprintf(&b, "%s; enum { _cgo_enum_%d = %s }; /* cgo-test:%d */\n", n.C, i, n.C, i)
    
    	b.WriteString("}\n")
    
    Russ Cox's avatar
    Russ Cox committed
    	stderr := p.gccErrors(b.Bytes())
    
    		fatalf("gcc produced no output\non input:\n%s", b.Bytes())
    
    Russ Cox's avatar
    Russ Cox committed
    	}
    
    	names := make([]*Name, len(toSniff))
    	copy(names, toSniff)
    
    	isConst := make([]bool, len(toSniff))
    	for i := range isConst {
    		isConst[i] = true // until proven otherwise
    
    Russ Cox's avatar
    Russ Cox committed
    
    
    	for _, line := range strings.Split(stderr, "\n", -1) {
    
    		if len(line) < 9 || line[0:9] != "cgo-test:" {
    
    			// the user will see any compiler errors when the code is compiled later.
    
    		line = line[9:]
    		colon := strings.Index(line, ":")
    
    		i, err := strconv.Atoi(line[0:colon])
    
    		case strings.Contains(line, ": useless type name in empty declaration"):
    
    Russ Cox's avatar
    Russ Cox committed
    			isConst[i] = false
    
    		case strings.Contains(line, ": statement with no effect"):
    
    Russ Cox's avatar
    Russ Cox committed
    			what = "not-type" // const or func or var
    
    		case strings.Contains(line, "undeclared"):
    
    			error(token.NoPos, "%s", strings.TrimSpace(line[colon+1:]))
    
    		case strings.Contains(line, "is not an integer constant"):
    
    Russ Cox's avatar
    Russ Cox committed
    			isConst[i] = false
    			continue
    
    Russ Cox's avatar
    Russ Cox committed
    		n := toSniff[i]
    		if n == nil {
    			continue
    
    Russ Cox's avatar
    Russ Cox committed
    		toSniff[i] = nil
    		n.Kind = what
    
    		j := len(needType)
    		needType = needType[0 : j+1]
    		needType[j] = n
    
    Russ Cox's avatar
    Russ Cox committed
    	for i, b := range isConst {
    		if b {
    			names[i].Kind = "const"
    
    Russ Cox's avatar
    Russ Cox committed
    	for _, n := range toSniff {
    		if n == nil {
    			continue
    		}
    		if n.Kind != "" {
    			continue
    		}
    
    		error(token.NoPos, "could not determine kind of name for C.%s", n.Go)
    
    Russ Cox's avatar
    Russ Cox committed
    	}
    
    		fatalf("unresolved names")
    
    Russ Cox's avatar
    Russ Cox committed
    	return needType
    }
    
    Russ Cox's avatar
    Russ Cox committed
    // loadDWARF parses the DWARF debug information generated
    // by gcc to learn the details of the constants, variables, and types
    // being referred to as C.xxx.
    func (p *Package) loadDWARF(f *File, names []*Name) {
    
    	// 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.
    
    Russ Cox's avatar
    Russ Cox committed
    	var b bytes.Buffer
    	b.WriteString(builtinProlog)
    	b.WriteString(f.Preamble)
    
    	for i, n := range names {
    
    Russ Cox's avatar
    Russ Cox committed
    		fmt.Fprintf(&b, "typeof(%s) *__cgo__%d;\n", n.C, i)
    		if n.Kind == "const" {
    			fmt.Fprintf(&b, "enum { __cgo_enum__%d = %s };\n", i, n.C)
    		}
    
    
    	// 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:]))
    	}
    
    Adam Langley's avatar
    Adam Langley committed
    	// Scan DWARF info for top-level TagVariable entries with AttrName __cgo__i.
    
    	types := make([]dwarf.Type, len(names))
    
    	enums := make([]dwarf.Offset, len(names))
    
    Russ Cox's avatar
    Russ Cox committed
    	nameToIndex := make(map[*Name]int)
    	for i, n := range names {
    		nameToIndex[n] = i
    	}
    
    			fatalf("reading DWARF entry: %s", err)
    
    		switch e.Tag {
    		case dwarf.TagEnumerationType:
    			offset := e.Offset
    			for {
    				e, err := r.Next()
    				if err != nil {
    
    					fatalf("reading DWARF entry: %s", err)
    
    				}
    				if e.Tag == 0 {
    					break
    				}
    				if e.Tag == dwarf.TagEnumerator {
    					entryName := e.Val(dwarf.AttrName).(string)
    
    Russ Cox's avatar
    Russ Cox committed
    					if strings.HasPrefix(entryName, "__cgo_enum__") {
    						n, _ := strconv.Atoi(entryName[len("__cgo_enum__"):])
    						if 0 <= n && n < len(names) {
    							enums[n] = offset
    						}
    
    					}
    				}
    			}
    		case dwarf.TagVariable:
    			name, _ := e.Val(dwarf.AttrName).(string)
    			typOff, _ := e.Val(dwarf.AttrType).(dwarf.Offset)
    			if name == "" || typOff == 0 {
    
    				fatalf("malformed DWARF TagVariable entry")
    
    			}
    			if !strings.HasPrefix(name, "__cgo__") {
    				break
    			}
    			typ, err := d.Type(typOff)
    			if err != nil {
    
    				fatalf("loading DWARF type: %s", err)
    
    			}
    			t, ok := typ.(*dwarf.PtrType)
    			if !ok || t == nil {
    
    				fatalf("internal error: %s has non-pointer type", name)
    
    			}
    			i, err := strconv.Atoi(name[7:])
    			if err != nil {
    
    				fatalf("malformed __cgo__ name: %s", name)
    
    			}
    			if enums[i] != 0 {
    				t, err := d.Type(enums[i])
    				if err != nil {
    
    					fatalf("loading DWARF type: %s", err)
    
    		}
    		if e.Tag != dwarf.TagCompileUnit {
    
    Russ Cox's avatar
    Russ Cox committed
    	// Record types and typedef information.
    
    	var conv typeConv
    	conv.Init(p.PtrSize)
    
    Russ Cox's avatar
    Russ Cox committed
    	for i, n := range names {
    
    		f, fok := types[i].(*dwarf.FuncType)
    
    Russ Cox's avatar
    Russ Cox committed
    		if n.Kind != "type" && fok {
    			n.Kind = "func"
    			n.FuncType = conv.FuncType(f)
    
    Russ Cox's avatar
    Russ Cox committed
    		} else {
    
    Russ Cox's avatar
    Russ Cox committed
    			n.Type = conv.Type(types[i])
    			if enums[i] != 0 && n.Type.EnumValues != nil {
    
    				k := fmt.Sprintf("__cgo_enum__%d", i)
    
    Russ Cox's avatar
    Russ Cox committed
    				n.Kind = "const"
    
    				n.Const = strconv.Itoa64(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
    
    			} else if n.Kind == "const" && i < len(enumVal) {
    				n.Const = strconv.Itoa64(enumVal[i])
    
    Russ Cox's avatar
    Russ Cox committed
    			}
    
    Russ Cox's avatar
    Russ Cox committed
    		}
    
    Russ Cox's avatar
    Russ Cox committed
    // 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.
    func (p *Package) rewriteRef(f *File) {
    	// Assign mangled names.
    	for _, n := range f.Name {
    		if n.Kind == "not-type" {
    			n.Kind = "var"
    		}
    		if n.Mangle == "" {
    			n.Mangle = "_C" + n.Kind + "_" + n.Go
    		}
    
    Russ Cox's avatar
    Russ Cox committed
    
    	// Now that we have all the name types filled in,
    	// scan through the Refs to identify the ones that
    	// 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)
    		}
    
    Russ Cox's avatar
    Russ Cox committed
    		var expr ast.Expr = ast.NewIdent(r.Name.Mangle) // default
    		switch r.Context {
    		case "call", "call2":
    			if r.Name.Kind != "func" {
    				if r.Name.Kind == "type" {
    					r.Context = "type"
    					expr = r.Name.Type.Go
    					break
    				}
    				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")
    				}
    				// Invent new Name for the two-result function.
    				n := f.Name["2"+r.Name.Go]
    				if n == nil {
    					n = new(Name)
    					*n = *r.Name
    					n.AddError = true
    					n.Mangle = "_C2func_" + n.Go
    					f.Name["2"+r.Name.Go] = n
    				}
    
    Russ Cox's avatar
    Russ Cox committed
    				r.Name = n
    				break
    			}
    		case "expr":
    			if r.Name.Kind == "func" {
    				error(r.Pos(), "must call C.%s", r.Name.Go)
    			}
    			if r.Name.Kind == "type" {
    				// Okay - might be new(T)
    				expr = r.Name.Type.Go
    			}
    			if r.Name.Kind == "var" {
    				expr = &ast.StarExpr{X: expr}
    			}
    
    		case "type":
    			if r.Name.Kind != "type" {
    				error(r.Pos(), "expression C.%s used as type", r.Name.Go)
    
    			} else {
    				expr = r.Name.Type.Go
    
    Russ Cox's avatar
    Russ Cox committed
    			}
    		default:
    			if r.Name.Kind == "func" {
    				error(r.Pos(), "must call C.%s", r.Name.Go)
    			}
    		}
    		*r.Expr = expr
    
    // gccName returns the name of the compiler to run.  Use $GCC if set in
    
    // the environment, otherwise just "gcc".
    
    func (p *Package) gccName() (ret string) {
    
    	if ret = os.Getenv("GCC"); ret == "" {
    
    Russ Cox's avatar
    Russ Cox committed
    // gccMachine returns the gcc -m flag to use, either "-m32" or "-m64".
    
    func (p *Package) gccMachine() []string {
    	switch runtime.GOARCH {
    	case "amd64":
    		return []string{"-m64"}
    	case "386":
    		return []string{"-m32"}
    	}
    	return nil
    
    Russ Cox's avatar
    Russ Cox committed
    }
    
    const gccTmp = "_obj/_cgo_.o"
    
    Russ Cox's avatar
    Russ Cox committed
    
    // gccCmd returns the gcc command line to use for compiling
    // the input.
    func (p *Package) gccCmd() []string {
    
    	c := []string{
    
    		"-Wall",                             // many warnings
    		"-Werror",                           // warnings are errors
    
    Russ Cox's avatar
    Russ Cox committed
    		"-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
    		"-xc",                               // input language is C
    
    	c = append(c, p.GccOptions...)
    
    	c = append(c, p.gccMachine()...)
    
    	c = append(c, "-") //read input from standard input
    	return c
    
    Russ Cox's avatar
    Russ Cox committed
    }
    
    // gccDebug runs gcc -gdwarf-2 over the C program stdin and
    
    // 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())
    
    	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:]
    							}
    						}
    					}
    				}
    
    	// 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
    
    
    	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")
    
    Russ Cox's avatar
    Russ Cox committed
    // gccDefines runs gcc -E -dM -xc - over the C program stdin
    // and returns the corresponding standard output, which is the
    // #defines that gcc encountered while processing the input
    // and its included files.
    func (p *Package) gccDefines(stdin []byte) string {
    
    	base := []string{p.gccName(), "-E", "-dM", "-xc"}
    	base = append(base, p.gccMachine()...)
    
    	stdout, _ := runGcc(stdin, append(append(base, p.GccOptions...), "-"))
    
    Russ Cox's avatar
    Russ Cox committed
    	return stdout
    }
    
    // gccErrors runs gcc over the C program stdin and returns
    // the errors that gcc prints.  That is, this function expects
    // gcc to fail.
    func (p *Package) gccErrors(stdin []byte) string {
    	// TODO(rsc): require failure
    
    	args := p.gccCmd()
    
    Russ Cox's avatar
    Russ Cox committed
    	if *debugGcc {
    		fmt.Fprintf(os.Stderr, "$ %s <<EOF\n", strings.Join(args, " "))
    		os.Stderr.Write(stdin)
    		fmt.Fprint(os.Stderr, "EOF\n")
    	}
    	stdout, stderr, _ := run(stdin, args)
    	if *debugGcc {
    		os.Stderr.Write(stdout)
    		os.Stderr.Write(stderr)
    
    Russ Cox's avatar
    Russ Cox committed
    	return string(stderr)
    }
    
    Russ Cox's avatar
    Russ Cox committed
    // runGcc runs the gcc command line args with stdin on standard input.
    // If the command exits with a non-zero exit status, runGcc prints
    // details about what was run and exits.
    // Otherwise runGcc returns the data written to standard output and standard error.
    // Note that for some of the uses we expect useful data back
    // on standard error, but for those uses gcc must still exit 0.
    func runGcc(stdin []byte, args []string) (string, string) {
    	if *debugGcc {
    		fmt.Fprintf(os.Stderr, "$ %s <<EOF\n", strings.Join(args, " "))
    		os.Stderr.Write(stdin)
    		fmt.Fprint(os.Stderr, "EOF\n")
    	}
    	stdout, stderr, ok := run(stdin, args)
    	if *debugGcc {
    		os.Stderr.Write(stdout)
    		os.Stderr.Write(stderr)
    	}
    
    Russ Cox's avatar
    Russ Cox committed
    		os.Stderr.Write(stderr)
    		os.Exit(2)
    
    Russ Cox's avatar
    Russ Cox committed
    	return string(stdout), string(stderr)
    
    Russ Cox's avatar
    Russ Cox committed
    // 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
    
    Russ Cox's avatar
    Russ Cox committed
    
    	// Predeclared types.
    
    	bool                                   ast.Expr
    
    	byte                                   ast.Expr // denotes padding
    	int8, int16, int32, int64              ast.Expr
    	uint8, uint16, uint32, uint64, uintptr ast.Expr
    	float32, float64                       ast.Expr
    
    	complex64, complex128                  ast.Expr
    
    	void                                   ast.Expr
    	unsafePointer                          ast.Expr
    	string                                 ast.Expr
    
    Russ Cox's avatar
    Russ Cox committed
    
    
    Russ Cox's avatar
    Russ Cox committed
    }
    
    
    var tagGen int
    
    var typedef = make(map[string]ast.Expr)
    
    Russ Cox's avatar
    Russ Cox committed
    func (c *typeConv) Init(ptrSize int64) {
    
    	c.ptrSize = ptrSize
    	c.m = make(map[dwarf.Type]*Type)
    
    	c.bool = c.Ident("bool")
    
    	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.complex64 = c.Ident("complex64")
    	c.complex128 = c.Ident("complex128")
    
    	c.unsafePointer = c.Ident("unsafe.Pointer")
    	c.void = c.Ident("void")
    	c.string = c.Ident("string")
    
    Russ Cox's avatar
    Russ Cox committed
    }
    
    // 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 {
    
    Russ Cox's avatar
    Russ Cox committed
    		}
    		if d, ok := dt.(*dwarf.TypedefType); ok {
    
    Russ Cox's avatar
    Russ Cox committed
    		}
    
    Russ Cox's avatar
    Russ Cox committed
    	}
    
    Russ Cox's avatar
    Russ Cox committed
    }
    
    // Map from dwarf text names to aliases we use in package "C".
    
    Russ Cox's avatar
    Russ Cox committed
    var dwarfToName = map[string]string{
    
    	"long int":               "long",
    	"long unsigned int":      "ulong",
    	"unsigned int":           "uint",
    	"short unsigned int":     "ushort",
    	"short int":              "short",
    	"long long int":          "longlong",
    
    Russ Cox's avatar
    Russ Cox committed
    	"long long unsigned int": "ulonglong",
    
    	"float complex":          "complexfloat",
    	"double complex":         "complexdouble",
    
    Robert Griesemer's avatar
    Robert Griesemer committed
    }
    
    Russ Cox's avatar
    Russ Cox committed
    
    
    const signedDelta = 64
    
    
    // String returns the current type representation.  Format arguments
    // are assembled within this method so that any changes in mutable
    // values are taken into account.
    func (tr *TypeRepr) String() string {
    	if len(tr.Repr) == 0 {
    		return ""
    	}
    	if len(tr.FormatArgs) == 0 {
    		return tr.Repr
    	}
    	return fmt.Sprintf(tr.Repr, tr.FormatArgs...)
    }
    
    // Empty returns true if the result of String would be "".
    func (tr *TypeRepr) Empty() bool {
    	return len(tr.Repr) == 0
    }
    
    // Set modifies the type representation.
    // If fargs are provided, repr is used as a format for fmt.Sprintf.
    // Otherwise, repr is used unprocessed as the type representation.
    func (tr *TypeRepr) Set(repr string, fargs ...interface{}) {
    	tr.Repr = repr
    	tr.FormatArgs = fargs
    }
    
    
    Russ Cox's avatar
    Russ Cox committed
    // 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 {
    
    			fatalf("type conversion loop at %s", dtype)
    
    Russ Cox's avatar
    Russ Cox committed
    		}
    
    Russ Cox's avatar
    Russ Cox committed
    	}
    
    
    	t := new(Type)
    	t.Size = dtype.Size()
    	t.Align = -1
    
    	t.C = &TypeRepr{Repr: dtype.Common().Name}
    
    Russ Cox's avatar
    Russ Cox committed
    
    
    Russ Cox's avatar
    Russ Cox committed
    	if t.Size < 0 {
    
    		t.Size = 0
    		t.Go = c.Opaque(0)
    
    		if t.C.Empty() {
    			t.C.Set("void")
    
    Russ Cox's avatar
    Russ Cox committed
    	}
    
    	switch dt := dtype.(type) {
    	default:
    
    		fatalf("unexpected type: %s", dtype)
    
    Russ Cox's avatar
    Russ Cox committed
    
    	case *dwarf.AddrType:
    		if t.Size != c.ptrSize {
    
    			fatalf("unexpected: %d-byte address type - %s", t.Size, dtype)
    
    Russ Cox's avatar
    Russ Cox committed
    		}
    
    		t.Go = c.uintptr
    		t.Align = t.Size
    
    Russ Cox's avatar
    Russ Cox committed
    
    	case *dwarf.ArrayType:
    		if dt.StrideBitSize > 0 {
    			// Cannot represent bit-sized elements in Go.
    
    			t.Go = c.Opaque(t.Size)
    			break
    
    Russ Cox's avatar
    Russ Cox committed
    		}
    		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.Set("typeof(%s[%d])", sub.C, dt.Count)
    
    Russ Cox's avatar
    Russ Cox committed
    
    
    	case *dwarf.BoolType:
    		t.Go = c.bool
    		t.Align = c.ptrSize
    
    
    Russ Cox's avatar
    Russ Cox committed
    	case *dwarf.CharType:
    		if t.Size != 1 {
    
    			fatalf("unexpected: %d-byte char type - %s", t.Size, dtype)
    
    Russ Cox's avatar
    Russ Cox committed
    		}
    
    		t.Go = c.int8
    		t.Align = 1
    
    Russ Cox's avatar
    Russ Cox committed
    
    	case *dwarf.EnumType:
    
    		if t.Align = t.Size; t.Align >= c.ptrSize {
    			t.Align = c.ptrSize
    		}