summaryrefslogtreecommitdiff
path: root/src/cmd/cgo/gcc.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/cgo/gcc.go')
-rw-r--r--src/cmd/cgo/gcc.go1367
1 files changed, 0 insertions, 1367 deletions
diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go
deleted file mode 100644
index a4d83f1e7..000000000
--- a/src/cmd/cgo/gcc.go
+++ /dev/null
@@ -1,1367 +0,0 @@
-// 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 Ref 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"
- "debug/pe"
- "encoding/binary"
- "flag"
- "fmt"
- "go/ast"
- "go/parser"
- "go/token"
- "os"
- "runtime"
- "strconv"
- "strings"
- "unicode"
-)
-
-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",
-}
-
-// 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")
- linesOut := make([]string, 0, len(linesIn))
-
-NextLine:
- 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.SplitN(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])
- if err != nil {
- 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.NewError("unclosed quote")
- } else if escaped {
- err = os.NewError("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
-}
-
-// 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) {
- var b bytes.Buffer
- b.WriteString(builtinProlog)
- b.WriteString(f.Preamble)
- stdout := p.gccDefines(b.Bytes())
-
- for _, line := range strings.Split(stdout, "\n") {
- 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:])
- }
-
- if n := f.Name[key]; n != nil {
- if *debugDefine {
- fmt.Fprintf(os.Stderr, "#define %s %s\n", key, val)
- }
- n.Define = val
- }
- }
-}
-
-// 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:
- // cgo-test:2: warning: useless type name in empty declaration
- // If name is a value, gcc will print
- // cgo-test:2: warning: statement with no effect
- // If name is undeclared, gcc will print
- // 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.
- //
- // 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)
- 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")
- 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")
- stderr := p.gccErrors(b.Bytes())
- if stderr == "" {
- fatalf("gcc produced no output\non input:\n%s", b.Bytes())
- }
-
- names := make([]*Name, len(toSniff))
- copy(names, toSniff)
-
- isConst := make([]bool, len(toSniff))
- for i := range isConst {
- isConst[i] = true // until proven otherwise
- }
-
- for _, line := range strings.Split(stderr, "\n") {
- if len(line) < 9 || line[0:9] != "cgo-test:" {
- // the user will see any compiler errors when the code is compiled later.
- continue
- }
- line = line[9:]
- 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.Contains(line, ": useless type name in empty declaration"):
- what = "type"
- isConst[i] = false
- case strings.Contains(line, ": statement with no effect"):
- 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"):
- isConst[i] = false
- continue
- }
- n := toSniff[i]
- if n == nil {
- continue
- }
- toSniff[i] = nil
- n.Kind = what
-
- j := len(needType)
- needType = needType[0 : j+1]
- needType[j] = n
- }
- for i, b := range isConst {
- if b {
- names[i].Kind = "const"
- }
- }
- 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)
- }
- if nerrors > 0 {
- fatalf("unresolved names")
- }
- return needType
-}
-
-// 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.
- var b bytes.Buffer
- b.WriteString(builtinProlog)
- b.WriteString(f.Preamble)
- for i, n := range names {
- 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:]))
- }
-
- // Scan DWARF info for top-level TagVariable entries with AttrName __cgo__i.
- types := make([]dwarf.Type, len(names))
- enums := make([]dwarf.Offset, len(names))
- nameToIndex := make(map[*Name]int)
- for i, n := range names {
- nameToIndex[n] = i
- }
- r := d.Reader()
- for {
- e, err := r.Next()
- if err != nil {
- fatalf("reading DWARF entry: %s", err)
- }
- if e == nil {
- break
- }
- 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)
- 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)
- }
- types[i] = t
- } else {
- types[i] = t.Type
- }
- }
- if e.Tag != dwarf.TagCompileUnit {
- r.SkipChildren()
- }
- }
-
- // Record types and typedef information.
- var conv typeConv
- conv.Init(p.PtrSize)
- for i, n := range names {
- f, fok := types[i].(*dwarf.FuncType)
- if n.Kind != "type" && fok {
- n.Kind = "func"
- n.FuncType = conv.FuncType(f)
- } else {
- n.Type = conv.Type(types[i])
- if enums[i] != 0 && n.Type.EnumValues != nil {
- k := fmt.Sprintf("__cgo_enum__%d", i)
- 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])
- }
- }
- }
-
-}
-
-// 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
- }
- }
-
- // 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)
- }
- 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
- }
- expr = ast.NewIdent(n.Mangle)
- 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
- }
- 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 == "" {
- ret = "gcc"
- }
- return
-}
-
-// 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
-}
-
-var gccTmp = objDir + "_cgo_.o"
-
-// gccCmd returns the gcc command line to use for compiling
-// the input.
-func (p *Package) gccCmd() []string {
- c := []string{
- p.gccName(),
- "-Wall", // many warnings
- "-Werror", // warnings are errors
- "-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
-}
-
-// 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:]
- }
- }
- }
- }
- }
- }
- return d, f.ByteOrder, data
- }
-
- // 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")
-}
-
-// 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...), "-"))
- 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()
- 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)
- }
- return string(stderr)
-}
-
-// 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)
- }
- if !ok {
- os.Stderr.Write(stderr)
- os.Exit(2)
- }
- return string(stdout), string(stderr)
-}
-
-// 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.
- 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
-
- ptrSize int64
-}
-
-var tagGen int
-var typedef = make(map[string]ast.Expr)
-
-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")
-}
-
-// 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 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",
- "long long unsigned int": "ulonglong",
- "signed char": "schar",
- "float complex": "complexfloat",
- "double complex": "complexdouble",
-}
-
-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
-}
-
-// 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)
- }
- return t
- }
-
- t := new(Type)
- t.Size = dtype.Size()
- t.Align = -1
- t.C = &TypeRepr{Repr: dtype.Common().Name}
- c.m[dtype] = t
-
- if t.Size < 0 {
- // Unsized types are [0]byte
- t.Size = 0
- t.Go = c.Opaque(0)
- if t.C.Empty() {
- t.C.Set("void")
- }
- return t
- }
-
- switch dt := dtype.(type) {
- default:
- fatalf("unexpected type: %s", dtype)
-
- case *dwarf.AddrType:
- if t.Size != c.ptrSize {
- fatalf("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.Set("typeof(%s[%d])", sub.C, dt.Count)
-
- case *dwarf.BoolType:
- t.Go = c.bool
- t.Align = c.ptrSize
-
- case *dwarf.CharType:
- if t.Size != 1 {
- fatalf("unexpected: %d-byte char type - %s", t.Size, dtype)
- }
- t.Go = c.int8
- t.Align = 1
-
- case *dwarf.EnumType:
- if t.Align = t.Size; t.Align >= c.ptrSize {
- t.Align = c.ptrSize
- }
- t.C.Set("enum " + dt.EnumName)
- signed := 0
- t.EnumValues = make(map[string]int64)
- for _, ev := range dt.Val {
- t.EnumValues[ev.Name] = ev.Val
- if ev.Val < 0 {
- signed = signedDelta
- }
- }
- switch t.Size + int64(signed) {
- default:
- fatalf("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
- case 1 + signedDelta:
- t.Go = c.int8
- case 2 + signedDelta:
- t.Go = c.int16
- case 4 + signedDelta:
- t.Go = c.int32
- case 8 + signedDelta:
- t.Go = c.int64
- }
-
- case *dwarf.FloatType:
- switch t.Size {
- default:
- fatalf("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.ComplexType:
- switch t.Size {
- default:
- fatalf("unexpected: %d-byte complex type - %s", t.Size, dtype)
- case 8:
- t.Go = c.complex64
- case 16:
- t.Go = c.complex128
- }
- 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 {
- fatalf("unexpected: %d-bit int type - %s", dt.BitSize, dtype)
- }
- switch t.Size {
- default:
- fatalf("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.Set("void*")
- break
- }
-
- gt := &ast.StarExpr{}
- t.Go = gt // publish before recursive call
- sub := c.Type(dt.Type)
- gt.X = sub.Go
- t.C.Set("%s*", 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(tagGen)
- tagGen++
- } else if t.C.Empty() {
- t.C.Set(dt.Kind + " " + tag)
- }
- name := c.Ident("_Ctype_" + dt.Kind + "_" + tag)
- t.Go = name // publish before recursive calls
- switch dt.Kind {
- case "union", "class":
- typedef[name.Name] = c.Opaque(t.Size)
- if t.C.Empty() {
- t.C.Set("typeof(unsigned char[%d])", t.Size)
- }
- case "struct":
- g, csyntax, align := c.Struct(dt)
- if t.C.Empty() {
- t.C.Set(csyntax)
- }
- t.Align = align
- typedef[name.Name] = 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("_Ctypedef_" + dt.Name)
- t.Go = name // publish before recursive call
- sub := c.Type(dt.Type)
- t.Size = sub.Size
- t.Align = sub.Align
- if _, ok := typedef[name.Name]; !ok {
- typedef[name.Name] = sub.Go
- }
-
- case *dwarf.UcharType:
- if t.Size != 1 {
- fatalf("unexpected: %d-byte uchar type - %s", t.Size, dtype)
- }
- t.Go = c.uint8
- t.Align = 1
-
- case *dwarf.UintType:
- if dt.BitSize > 0 {
- fatalf("unexpected: %d-bit uint type - %s", dt.BitSize, dtype)
- }
- switch t.Size {
- default:
- fatalf("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.Set("void")
- }
-
- switch dtype.(type) {
- case *dwarf.AddrType, *dwarf.BoolType, *dwarf.CharType, *dwarf.IntType, *dwarf.FloatType, *dwarf.UcharType, *dwarf.UintType:
- s := dtype.Common().Name
- if s != "" {
- if ss, ok := dwarfToName[s]; ok {
- s = ss
- }
- s = strings.Join(strings.Split(s, " "), "") // strip spaces
- name := c.Ident("_Ctype_" + s)
- typedef[name.Name] = t.Go
- t.Go = name
- }
- }
-
- if t.C.Empty() {
- fatalf("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.
- tr := &TypeRepr{}
- tr.Set("%s*", t.C)
- return &Type{
- Size: c.ptrSize,
- Align: c.ptrSize,
- Go: &ast.StarExpr{X: t.Go},
- C: tr,
- }
- 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 {
- // Unless the typedef happens to point to void* since
- // Go has special rules around using unsafe.Pointer.
- if _, void := base(ptr.Type).(*dwarf.VoidType); !void {
- 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 {
- // gcc's DWARF generator outputs a single DotDotDotType parameter for
- // function pointers that specify no parameters (e.g. void
- // (*__cgo_0)()). Treat this special case as void. This case is
- // invalid according to ISO C anyway (i.e. void (*__cgo_1)(...) is not
- // legal).
- if _, ok := f.(*dwarf.DotDotDotType); ok && i == 0 {
- p, gp = nil, nil
- break
- }
- 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: &ast.FieldList{List: gp},
- Results: &ast.FieldList{List: gr},
- },
- }
-}
-
-// Identifier
-func (c *typeConv) Ident(s string) *ast.Ident {
- return ast.NewIdent(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: 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: return Go and (6g) C syntax for type.
-func (c *typeConv) Struct(dt *dwarf.StructType) (expr *ast.StructType, csyntax string, align int64) {
- var buf bytes.Buffer
- buf.WriteString("struct {")
- fld := make([]*ast.Field, 0, 2*len(dt.Field)+1) // enough for padding around every field
- off := int64(0)
-
- // Rename struct fields that happen to be named Go keywords into
- // _{keyword}. Create a map from C ident -> Go ident. The Go ident will
- // be mangled. Any existing identifier that already has the same name on
- // the C-side will cause the Go-mangled version to be prefixed with _.
- // (e.g. in a struct with fields '_type' and 'type', the latter would be
- // rendered as '__type' in Go).
- ident := make(map[string]string)
- used := make(map[string]bool)
- for _, f := range dt.Field {
- ident[f.Name] = f.Name
- used[f.Name] = true
- }
- for cid, goid := range ident {
- if token.Lookup([]byte(goid)).IsKeyword() {
- // Avoid keyword
- goid = "_" + goid
-
- // Also avoid existing fields
- for _, exist := used[goid]; exist; _, exist = used[goid] {
- goid = "_" + goid
- }
-
- used[goid] = true
- ident[cid] = goid
- }
- }
-
- for _, f := range dt.Field {
- if f.BitSize > 0 && f.BitSize != f.ByteSize*8 {
- continue
- }
- 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(ident[f.Name])}, Type: t.Go}
- off += t.Size
- buf.WriteString(t.C.String())
- buf.WriteString(" ")
- buf.WriteString(f.Name)
- buf.WriteString("; ")
- 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 {
- fatalf("struct size calculation error")
- }
- buf.WriteString("}")
- csyntax = buf.String()
- expr = &ast.StructType{Fields: &ast.FieldList{List: fld}}
- return
-}