// 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" "errors" "flag" "fmt" "go/ast" "go/parser" "go/token" "internal/xcoff" "math" "os" "os/exec" "strconv" "strings" "unicode" "unicode/utf8" "cmd/internal/quoted" ) 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", } var incomplete = "_cgopackage.Incomplete" // 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_"):] } if strings.HasPrefix(s, "sizeof_") { return "sizeof(" + cname(s[len("sizeof_"):]) + ")" } return s } // ProcessCgoDirectives processes the import C preamble: // 1. discards all #cgo CFLAGS, LDFLAGS, nocallback and noescape directives, // so they don't make their way into _cgo_export.h. // 2. parse the nocallback and noescape directives. func (f *File) ProcessCgoDirectives() { linesIn := strings.Split(f.Preamble, "\n") linesOut := make([]string, 0, len(linesIn)) f.NoCallbacks = make(map[string]bool) f.NoEscapes = make(map[string]bool) for _, line := range linesIn { l := strings.TrimSpace(line) if len(l) < 5 || l[:4] != "#cgo" || !unicode.IsSpace(rune(l[4])) { linesOut = append(linesOut, line) } else { linesOut = append(linesOut, "") // #cgo (nocallback|noescape) if fields := strings.Fields(l); len(fields) == 3 { directive := fields[1] funcName := fields[2] if directive == "nocallback" { fatalf("#cgo nocallback disabled until Go 1.23") f.NoCallbacks[funcName] = true } else if directive == "noescape" { fatalf("#cgo noescape disabled until Go 1.23") f.NoEscapes[funcName] = true } } } } f.Preamble = strings.Join(linesOut, "\n") } // addToFlag appends args to flag. func (p *Package) addToFlag(flag string, args []string) { if flag == "CFLAGS" { // We'll also need these when preprocessing for dwarf information. // However, discard any -g options: we need to be able // to parse the debug info, so stick to what we expect. for _, arg := range args { if !strings.HasPrefix(arg, "-g") { p.GccOptions = append(p.GccOptions, arg) } } } if flag == "LDFLAGS" { p.LdFlags = append(p.LdFlags, args...) } } // 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 error) { var args []string arg := make([]rune, len(s)) escaped := false quoted := false quote := '\x00' i := 0 for _, r := range s { switch { case escaped: escaped = false case r == '\\': escaped = true continue case quote != 0: if r == quote { quote = 0 continue } case r == '"' || r == '\'': quoted = true quote = r continue case unicode.IsSpace(r): if quoted || i > 0 { quoted = false args = append(args, string(arg[:i])) i = 0 } continue } arg[i] = r i++ } if quoted || i > 0 { args = append(args, string(arg[:i])) } if quote != 0 { err = errors.New("unclosed quote") } else if escaped { err = errors.New("unfinished escaping") } return args, err } // 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) } var conv typeConv conv.Init(p.PtrSize, p.IntSize) p.loadDefines(f) p.typedefs = map[string]bool{} p.typedefList = nil numTypedefs := -1 for len(p.typedefs) > numTypedefs { numTypedefs = len(p.typedefs) // Also ask about any typedefs we've seen so far. for _, info := range p.typedefList { if f.Name[info.typedef] != nil { continue } n := &Name{ Go: info.typedef, C: info.typedef, } f.Name[info.typedef] = n f.NamePos[n] = info.pos } needType := p.guessKinds(f) if len(needType) > 0 { p.loadDWARF(f, &conv, needType) } // In godefs mode we're OK with the typedefs, which // will presumably also be defined in the file, we // don't want to resolve them to their base types. if *godefs { break } } p.prepareNames(f) if p.rewriteCalls(f) { // Add `import _cgo_unsafe "unsafe"` after the package statement. f.Edit.Insert(f.offset(f.AST.Name.End()), "; import _cgo_unsafe \"unsafe\"") } 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 key == "__clang__" { p.GccIsClang = true } 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 { // Determine kinds for names we already know about, // like #defines or 'struct foo', before bothering with gcc. var names, needType []*Name optional := map[*Name]bool{} for _, key := range nameKeys(f.Name) { n := f.Name[key] // If we've already found this name as a #define // and we can translate it as a constant value, do so. if n.Define != "" { if i, err := strconv.ParseInt(n.Define, 0, 64); err == nil { n.Kind = "iconst" // Turn decimal into hex, just for consistency // with enum-derived constants. Otherwise // in the cgo -godefs output half the constants // are in hex and half are in whatever the #define used. n.Const = fmt.Sprintf("%#x", i) } else if n.Define[0] == '\'' { if _, err := parser.ParseExpr(n.Define); err == nil { n.Kind = "iconst" n.Const = n.Define } } else if n.Define[0] == '"' { if _, err := parser.ParseExpr(n.Define); err == nil { n.Kind = "sconst" n.Const = n.Define } } if n.IsConst() { continue } } // If this is a struct, union, or enum type name, no need to guess the kind. if strings.HasPrefix(n.C, "struct ") || strings.HasPrefix(n.C, "union ") || strings.HasPrefix(n.C, "enum ") { n.Kind = "type" needType = append(needType, n) continue } if (goos == "darwin" || goos == "ios") && strings.HasSuffix(n.C, "Ref") { // For FooRef, find out if FooGetTypeID exists. s := n.C[:len(n.C)-3] + "GetTypeID" n := &Name{Go: s, C: s} names = append(names, n) optional[n] = true } // Otherwise, we'll need to find out from gcc. names = append(names, n) } // Bypass gcc if there's nothing left to find out. if len(names) == 0 { return needType } // Coerce gcc into telling us whether each name is a type, a value, or undeclared. // For names, find out whether they are integer constants. // We used to look at specific warning or error messages here, but that tied the // behavior too closely to specific versions of the compilers. // Instead, arrange that we can infer what we need from only the presence or absence // of an error on a specific line. // // For each name, we generate these lines, where xxx is the index in toSniff plus one. // // #line xxx "not-declared" // void __cgo_f_xxx_1(void) { __typeof__(name) *__cgo_undefined__1; } // #line xxx "not-type" // void __cgo_f_xxx_2(void) { name *__cgo_undefined__2; } // #line xxx "not-int-const" // void __cgo_f_xxx_3(void) { enum { __cgo_undefined__3 = (name)*1 }; } // #line xxx "not-num-const" // void __cgo_f_xxx_4(void) { static const double __cgo_undefined__4 = (name); } // #line xxx "not-str-lit" // void __cgo_f_xxx_5(void) { static const char __cgo_undefined__5[] = (name); } // // If we see an error at not-declared:xxx, the corresponding name is not declared. // If we see an error at not-type:xxx, the corresponding name is not a type. // If we see an error at not-int-const:xxx, the corresponding name is not an integer constant. // If we see an error at not-num-const:xxx, the corresponding name is not a number constant. // If we see an error at not-str-lit:xxx, the corresponding name is not a string literal. // // The specific input forms are chosen so that they are valid C syntax regardless of // whether name denotes a type or an expression. var b bytes.Buffer b.WriteString(builtinProlog) b.WriteString(f.Preamble) for i, n := range names { fmt.Fprintf(&b, "#line %d \"not-declared\"\n"+ "void __cgo_f_%d_1(void) { __typeof__(%s) *__cgo_undefined__1; }\n"+ "#line %d \"not-type\"\n"+ "void __cgo_f_%d_2(void) { %s *__cgo_undefined__2; }\n"+ "#line %d \"not-int-const\"\n"+ "void __cgo_f_%d_3(void) { enum { __cgo_undefined__3 = (%s)*1 }; }\n"+ "#line %d \"not-num-const\"\n"+ "void __cgo_f_%d_4(void) { static const double __cgo_undefined__4 = (%s); }\n"+ "#line %d \"not-str-lit\"\n"+ "void __cgo_f_%d_5(void) { static const char __cgo_undefined__5[] = (%s); }\n", i+1, i+1, n.C, i+1, i+1, n.C, i+1, i+1, n.C, i+1, i+1, n.C, i+1, i+1, n.C, ) } fmt.Fprintf(&b, "#line 1 \"completed\"\n"+ "int __cgo__1 = __cgo__2;\n") // We need to parse the output from this gcc command, so ensure that it // doesn't have any ANSI escape sequences in it. (TERM=dumb is // insufficient; if the user specifies CGO_CFLAGS=-fdiagnostics-color, // GCC will ignore TERM, and GCC can also be configured at compile-time // to ignore TERM.) stderr := p.gccErrors(b.Bytes(), "-fdiagnostics-color=never") if strings.Contains(stderr, "unrecognized command line option") { // We're using an old version of GCC that doesn't understand // -fdiagnostics-color. Those versions can't print color anyway, // so just rerun without that option. stderr = p.gccErrors(b.Bytes()) } if stderr == "" { fatalf("%s produced no output\non input:\n%s", gccBaseCmd[0], b.Bytes()) } completed := false sniff := make([]int, len(names)) const ( notType = 1 << iota notIntConst notNumConst notStrLiteral notDeclared ) sawUnmatchedErrors := false for _, line := range strings.Split(stderr, "\n") { // Ignore warnings and random comments, with one // exception: newer GCC versions will sometimes emit // an error on a macro #define with a note referring // to where the expansion occurs. We care about where // the expansion occurs, so in that case treat the note // as an error. isError := strings.Contains(line, ": error:") isErrorNote := strings.Contains(line, ": note:") && sawUnmatchedErrors if !isError && !isErrorNote { continue } c1 := strings.Index(line, ":") if c1 < 0 { continue } c2 := strings.Index(line[c1+1:], ":") if c2 < 0 { continue } c2 += c1 + 1 filename := line[:c1] i, _ := strconv.Atoi(line[c1+1 : c2]) i-- if i < 0 || i >= len(names) { if isError { sawUnmatchedErrors = true } continue } switch filename { case "completed": // Strictly speaking, there is no guarantee that seeing the error at completed:1 // (at the end of the file) means we've seen all the errors from earlier in the file, // but usually it does. Certainly if we don't see the completed:1 error, we did // not get all the errors we expected. completed = true case "not-declared": sniff[i] |= notDeclared case "not-type": sniff[i] |= notType case "not-int-const": sniff[i] |= notIntConst case "not-num-const": sniff[i] |= notNumConst case "not-str-lit": sniff[i] |= notStrLiteral default: if isError { sawUnmatchedErrors = true } continue } sawUnmatchedErrors = false } if !completed { fatalf("%s did not produce error at completed:1\non input:\n%s\nfull error output:\n%s", gccBaseCmd[0], b.Bytes(), stderr) } for i, n := range names { switch sniff[i] { default: if sniff[i]¬Declared != 0 && optional[n] { // Ignore optional undeclared identifiers. // Don't report an error, and skip adding n to the needType array. continue } error_(f.NamePos[n], "could not determine kind of name for C.%s", fixGo(n.Go)) case notStrLiteral | notType: n.Kind = "iconst" case notIntConst | notStrLiteral | notType: n.Kind = "fconst" case notIntConst | notNumConst | notType: n.Kind = "sconst" case notIntConst | notNumConst | notStrLiteral: n.Kind = "type" case notIntConst | notNumConst | notStrLiteral | notType: n.Kind = "not-type" } needType = append(needType, n) } if nerrors > 0 { // Check if compiling the preamble by itself causes any errors, // because the messages we've printed out so far aren't helpful // to users debugging preamble mistakes. See issue 8442. preambleErrors := p.gccErrors([]byte(builtinProlog + f.Preamble)) if len(preambleErrors) > 0 { error_(token.NoPos, "\n%s errors for preamble:\n%s", gccBaseCmd[0], preambleErrors) } 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, conv *typeConv, 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) b.WriteString("#line 1 \"cgo-dwarf-inference\"\n") for i, n := range names { fmt.Fprintf(&b, "__typeof__(%s) *__cgo__%d;\n", n.C, i) if n.Kind == "iconst" { fmt.Fprintf(&b, "enum { __cgo_enum__%d = %s };\n", i, n.C) } } // We create a data block initialized with the values, // so we can read them out of the object file. fmt.Fprintf(&b, "long long __cgodebug_ints[] = {\n") for _, n := range names { if n.Kind == "iconst" { fmt.Fprintf(&b, "\t%s,\n", n.C) } else { fmt.Fprintf(&b, "\t0,\n") } } // for the last entry, we cannot use 0, otherwise // in case all __cgodebug_data is zero initialized, // LLVM-based gcc will place the it in the __DATA.__common // zero-filled section (our debug/macho doesn't support // this) fmt.Fprintf(&b, "\t1\n") fmt.Fprintf(&b, "};\n") // do the same work for floats. fmt.Fprintf(&b, "double __cgodebug_floats[] = {\n") for _, n := range names { if n.Kind == "fconst" { fmt.Fprintf(&b, "\t%s,\n", n.C) } else { fmt.Fprintf(&b, "\t0,\n") } } fmt.Fprintf(&b, "\t1\n") fmt.Fprintf(&b, "};\n") // do the same work for strings. for i, n := range names { if n.Kind == "sconst" { fmt.Fprintf(&b, "const char __cgodebug_str__%d[] = %s;\n", i, n.C) fmt.Fprintf(&b, "const unsigned long long __cgodebug_strlen__%d = sizeof(%s)-1;\n", i, n.C) } } d, ints, floats, strs := p.gccDebug(b.Bytes(), len(names)) // Scan DWARF info for top-level TagVariable entries with AttrName __cgo__i. types := make([]dwarf.Type, len(names)) 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.TagVariable: name, _ := e.Val(dwarf.AttrName).(string) // As of https://reviews.llvm.org/D123534, clang // now emits DW_TAG_variable DIEs that have // no name (so as to be able to describe the // type and source locations of constant strings) // like the second arg in the call below: // // myfunction(42, "foo") // // If a var has no name we won't see attempts to // refer to it via "C.", so skip these vars // // See issue 53000 for more context. if name == "" { break } typOff, _ := e.Val(dwarf.AttrType).(dwarf.Offset) if typOff == 0 { if e.Val(dwarf.AttrSpecification) != nil { // Since we are reading all the DWARF, // assume we will see the variable elsewhere. break } 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) } types[i] = t.Type p.recordTypedefs(t.Type, f.NamePos[names[i]]) } if e.Tag != dwarf.TagCompileUnit { r.SkipChildren() } } // Record types and typedef information. for i, n := range names { if strings.HasSuffix(n.Go, "GetTypeID") && types[i].String() == "func() CFTypeID" { conv.getTypeIDs[n.Go[:len(n.Go)-9]] = true } } for i, n := range names { if types[i] == nil { continue } pos := f.NamePos[n] f, fok := types[i].(*dwarf.FuncType) if n.Kind != "type" && fok { n.Kind = "func" n.FuncType = conv.FuncType(f, pos) } else { n.Type = conv.Type(types[i], pos) switch n.Kind { case "iconst": if i < len(ints) { if _, ok := types[i].(*dwarf.UintType); ok { n.Const = fmt.Sprintf("%#x", uint64(ints[i])) } else { n.Const = fmt.Sprintf("%#x", ints[i]) } } case "fconst": if i >= len(floats) { break } switch base(types[i]).(type) { case *dwarf.IntType, *dwarf.UintType: // This has an integer type so it's // not really a floating point // constant. This can happen when the // C compiler complains about using // the value as an integer constant, // but not as a general constant. // Treat this as a variable of the // appropriate type, not a constant, // to get C-style type handling, // avoiding the problem that C permits // uint64(-1) but Go does not. // See issue 26066. n.Kind = "var" default: n.Const = fmt.Sprintf("%f", floats[i]) } case "sconst": if i < len(strs) { n.Const = fmt.Sprintf("%q", strs[i]) } } } conv.FinishType(pos) } } // recordTypedefs remembers in p.typedefs all the typedefs used in dtypes and its children. func (p *Package) recordTypedefs(dtype dwarf.Type, pos token.Pos) { p.recordTypedefs1(dtype, pos, map[dwarf.Type]bool{}) } func (p *Package) recordTypedefs1(dtype dwarf.Type, pos token.Pos, visited map[dwarf.Type]bool) { if dtype == nil { return } if visited[dtype] { return } visited[dtype] = true switch dt := dtype.(type) { case *dwarf.TypedefType: if strings.HasPrefix(dt.Name, "__builtin") { // Don't look inside builtin types. There be dragons. return } if !p.typedefs[dt.Name] { p.typedefs[dt.Name] = true p.typedefList = append(p.typedefList, typedefInfo{dt.Name, pos}) p.recordTypedefs1(dt.Type, pos, visited) } case *dwarf.PtrType: p.recordTypedefs1(dt.Type, pos, visited) case *dwarf.ArrayType: p.recordTypedefs1(dt.Type, pos, visited) case *dwarf.QualType: p.recordTypedefs1(dt.Type, pos, visited) case *dwarf.FuncType: p.recordTypedefs1(dt.ReturnType, pos, visited) for _, a := range dt.ParamType { p.recordTypedefs1(a, pos, visited) } case *dwarf.StructType: for _, f := range dt.Field { p.recordTypedefs1(f.Type, pos, visited) } } } // prepareNames finalizes the Kind field of not-type names and sets // the mangled name of all names. func (p *Package) prepareNames(f *File) { for _, n := range f.Name { if n.Kind == "not-type" { if n.Define == "" { n.Kind = "var" } else { n.Kind = "macro" n.FuncType = &FuncType{ Result: n.Type, Go: &ast.FuncType{ Results: &ast.FieldList{List: []*ast.Field{{Type: n.Type.Go}}}, }, } } } p.mangleName(n) if n.Kind == "type" && typedef[n.Mangle] == nil { typedef[n.Mangle] = n.Type } } } // mangleName does name mangling to translate names // from the original Go source files to the names // used in the final Go files generated by cgo. func (p *Package) mangleName(n *Name) { // When using gccgo variables have to be // exported so that they become global symbols // that the C code can refer to. prefix := "_C" if *gccgo && n.IsVar() { prefix = "C" } n.Mangle = prefix + n.Kind + "_" + n.Go } func (f *File) isMangledName(s string) bool { prefix := "_C" if strings.HasPrefix(s, prefix) { t := s[len(prefix):] for _, k := range nameKinds { if strings.HasPrefix(t, k+"_") { return true } } } return false } // rewriteCalls rewrites all calls that pass pointers to check that // they follow the rules for passing pointers between Go and C. // This reports whether the package needs to import unsafe as _cgo_unsafe. func (p *Package) rewriteCalls(f *File) bool { needsUnsafe := false // Walk backward so that in C.f1(C.f2()) we rewrite C.f2 first. for _, call := range f.Calls { if call.Done { continue } start := f.offset(call.Call.Pos()) end := f.offset(call.Call.End()) str, nu := p.rewriteCall(f, call) if str != "" { f.Edit.Replace(start, end, str) if nu { needsUnsafe = true } } } return needsUnsafe } // rewriteCall rewrites one call to add pointer checks. // If any pointer checks are required, we rewrite the call into a // function literal that calls _cgoCheckPointer for each pointer // argument and then calls the original function. // This returns the rewritten call and whether the package needs to // import unsafe as _cgo_unsafe. // If it returns the empty string, the call did not need to be rewritten. func (p *Package) rewriteCall(f *File, call *Call) (string, bool) { // This is a call to C.xxx; set goname to "xxx". // It may have already been mangled by rewriteName. var goname string switch fun := call.Call.Fun.(type) { case *ast.SelectorExpr: goname = fun.Sel.Name case *ast.Ident: goname = strings.TrimPrefix(fun.Name, "_C2func_") goname = strings.TrimPrefix(goname, "_Cfunc_") } if goname == "" || goname == "malloc" { return "", false } name := f.Name[goname] if name == nil || name.Kind != "func" { // Probably a type conversion. return "", false } params := name.FuncType.Params args := call.Call.Args end := call.Call.End() // Avoid a crash if the number of arguments doesn't match // the number of parameters. // This will be caught when the generated file is compiled. if len(args) != len(params) { return "", false } any := false for i, param := range params { if p.needsPointerCheck(f, param.Go, args[i]) { any = true break } } if !any { return "", false } // We need to rewrite this call. // // Rewrite C.f(p) to // func() { // _cgo0 := p // _cgoCheckPointer(_cgo0, nil) // C.f(_cgo0) // }() // Using a function literal like this lets us evaluate the // function arguments only once while doing pointer checks. // This is particularly useful when passing additional arguments // to _cgoCheckPointer, as done in checkIndex and checkAddr. // // When the function argument is a conversion to unsafe.Pointer, // we unwrap the conversion before checking the pointer, // and then wrap again when calling C.f. This lets us check // the real type of the pointer in some cases. See issue #25941. // // When the call to C.f is deferred, we use an additional function // literal to evaluate the arguments at the right time. // defer func() func() { // _cgo0 := p // return func() { // _cgoCheckPointer(_cgo0, nil) // C.f(_cgo0) // } // }()() // This works because the defer statement evaluates the first // function literal in order to get the function to call. var sb bytes.Buffer sb.WriteString("func() ") if call.Deferred { sb.WriteString("func() ") } needsUnsafe := false result := false twoResults := false if !call.Deferred { // Check whether this call expects two results. for _, ref := range f.Ref { if ref.Expr != &call.Call.Fun { continue } if ref.Context == ctxCall2 { sb.WriteString("(") result = true twoResults = true } break } // Add the result type, if any. if name.FuncType.Result != nil { rtype := p.rewriteUnsafe(name.FuncType.Result.Go) if rtype != name.FuncType.Result.Go { needsUnsafe = true } sb.WriteString(gofmtLine(rtype)) result = true } // Add the second result type, if any. if twoResults { if name.FuncType.Result == nil { // An explicit void result looks odd but it // seems to be how cgo has worked historically. sb.WriteString("_Ctype_void") } sb.WriteString(", error)") } } sb.WriteString("{ ") // Define _cgoN for each argument value. // Write _cgoCheckPointer calls to sbCheck. var sbCheck bytes.Buffer for i, param := range params { origArg := args[i] arg, nu := p.mangle(f, &args[i], true) if nu { needsUnsafe = true } // Use "var x T = ..." syntax to explicitly convert untyped // constants to the parameter type, to avoid a type mismatch. ptype := p.rewriteUnsafe(param.Go) if !p.needsPointerCheck(f, param.Go, args[i]) || param.BadPointer || p.checkUnsafeStringData(args[i]) { if ptype != param.Go { needsUnsafe = true } fmt.Fprintf(&sb, "var _cgo%d %s = %s; ", i, gofmtLine(ptype), gofmtPos(arg, origArg.Pos())) continue } // Check for &a[i]. if p.checkIndex(&sb, &sbCheck, arg, i) { continue } // Check for &x. if p.checkAddr(&sb, &sbCheck, arg, i) { continue } // Check for a[:]. if p.checkSlice(&sb, &sbCheck, arg, i) { continue } fmt.Fprintf(&sb, "_cgo%d := %s; ", i, gofmtPos(arg, origArg.Pos())) fmt.Fprintf(&sbCheck, "_cgoCheckPointer(_cgo%d, nil); ", i) } if call.Deferred { sb.WriteString("return func() { ") } // Write out the calls to _cgoCheckPointer. sb.WriteString(sbCheck.String()) if result { sb.WriteString("return ") } m, nu := p.mangle(f, &call.Call.Fun, false) if nu { needsUnsafe = true } sb.WriteString(gofmtPos(m, end)) sb.WriteString("(") for i := range params { if i > 0 { sb.WriteString(", ") } fmt.Fprintf(&sb, "_cgo%d", i) } sb.WriteString("); ") if call.Deferred { sb.WriteString("}") } sb.WriteString("}") if call.Deferred { sb.WriteString("()") } sb.WriteString("()") return sb.String(), needsUnsafe } // needsPointerCheck reports whether the type t needs a pointer check. // This is true if t is a pointer and if the value to which it points // might contain a pointer. func (p *Package) needsPointerCheck(f *File, t ast.Expr, arg ast.Expr) bool { // An untyped nil does not need a pointer check, and when // _cgoCheckPointer returns the untyped nil the type assertion we // are going to insert will fail. Easier to just skip nil arguments. // TODO: Note that this fails if nil is shadowed. if id, ok := arg.(*ast.Ident); ok && id.Name == "nil" { return false } return p.hasPointer(f, t, true) } // hasPointer is used by needsPointerCheck. If top is true it returns // whether t is or contains a pointer that might point to a pointer. // If top is false it reports whether t is or contains a pointer. // f may be nil. func (p *Package) hasPointer(f *File, t ast.Expr, top bool) bool { switch t := t.(type) { case *ast.ArrayType: if t.Len == nil { if !top { return true } return p.hasPointer(f, t.Elt, false) } return p.hasPointer(f, t.Elt, top) case *ast.StructType: for _, field := range t.Fields.List { if p.hasPointer(f, field.Type, top) { return true } } return false case *ast.StarExpr: // Pointer type. if !top { return true } // Check whether this is a pointer to a C union (or class) // type that contains a pointer. if unionWithPointer[t.X] { return true } return p.hasPointer(f, t.X, false) case *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.ChanType: return true case *ast.Ident: // TODO: Handle types defined within function. for _, d := range p.Decl { gd, ok := d.(*ast.GenDecl) if !ok || gd.Tok != token.TYPE { continue } for _, spec := range gd.Specs { ts, ok := spec.(*ast.TypeSpec) if !ok { continue } if ts.Name.Name == t.Name { return p.hasPointer(f, ts.Type, top) } } } if def := typedef[t.Name]; def != nil { return p.hasPointer(f, def.Go, top) } if t.Name == "string" { return !top } if t.Name == "error" { return true } if goTypes[t.Name] != nil { return false } // We can't figure out the type. Conservative // approach is to assume it has a pointer. return true case *ast.SelectorExpr: if l, ok := t.X.(*ast.Ident); !ok || l.Name != "C" { // Type defined in a different package. // Conservative approach is to assume it has a // pointer. return true } if f == nil { // Conservative approach: assume pointer. return true } name := f.Name[t.Sel.Name] if name != nil && name.Kind == "type" && name.Type != nil && name.Type.Go != nil { return p.hasPointer(f, name.Type.Go, top) } // We can't figure out the type. Conservative // approach is to assume it has a pointer. return true default: error_(t.Pos(), "could not understand type %s", gofmt(t)) return true } } // mangle replaces references to C names in arg with the mangled names, // rewriting calls when it finds them. // It removes the corresponding references in f.Ref and f.Calls, so that we // don't try to do the replacement again in rewriteRef or rewriteCall. // If addPosition is true, add position info to the idents of C names in arg. func (p *Package) mangle(f *File, arg *ast.Expr, addPosition bool) (ast.Expr, bool) { needsUnsafe := false f.walk(arg, ctxExpr, func(f *File, arg interface{}, context astContext) { px, ok := arg.(*ast.Expr) if !ok { return } sel, ok := (*px).(*ast.SelectorExpr) if ok { if l, ok := sel.X.(*ast.Ident); !ok || l.Name != "C" { return } for _, r := range f.Ref { if r.Expr == px { *px = p.rewriteName(f, r, addPosition) r.Done = true break } } return } call, ok := (*px).(*ast.CallExpr) if !ok { return } for _, c := range f.Calls { if !c.Done && c.Call.Lparen == call.Lparen { cstr, nu := p.rewriteCall(f, c) if cstr != "" { // Smuggle the rewritten call through an ident. *px = ast.NewIdent(cstr) if nu { needsUnsafe = true } c.Done = true } } } }) return *arg, needsUnsafe } // checkIndex checks whether arg has the form &a[i], possibly inside // type conversions. If so, then in the general case it writes // // _cgoIndexNN := a // _cgoNN := &cgoIndexNN[i] // with type conversions, if any // // to sb, and writes // // _cgoCheckPointer(_cgoNN, _cgoIndexNN) // // to sbCheck, and returns true. If a is a simple variable or field reference, // it writes // // _cgoIndexNN := &a // // and dereferences the uses of _cgoIndexNN. Taking the address avoids // making a copy of an array. // // This tells _cgoCheckPointer to check the complete contents of the // slice or array being indexed, but no other part of the memory allocation. func (p *Package) checkIndex(sb, sbCheck *bytes.Buffer, arg ast.Expr, i int) bool { // Strip type conversions. x := arg for { c, ok := x.(*ast.CallExpr) if !ok || len(c.Args) != 1 { break } if !p.isType(c.Fun) && !p.isUnsafeData(c.Fun, false) { break } x = c.Args[0] } u, ok := x.(*ast.UnaryExpr) if !ok || u.Op != token.AND { return false } index, ok := u.X.(*ast.IndexExpr) if !ok { return false } addr := "" deref := "" if p.isVariable(index.X) { addr = "&" deref = "*" } fmt.Fprintf(sb, "_cgoIndex%d := %s%s; ", i, addr, gofmtPos(index.X, index.X.Pos())) origX := index.X index.X = ast.NewIdent(fmt.Sprintf("_cgoIndex%d", i)) if deref == "*" { index.X = &ast.StarExpr{X: index.X} } fmt.Fprintf(sb, "_cgo%d := %s; ", i, gofmtPos(arg, arg.Pos())) index.X = origX fmt.Fprintf(sbCheck, "_cgoCheckPointer(_cgo%d, %s_cgoIndex%d); ", i, deref, i) return true } // checkAddr checks whether arg has the form &x, possibly inside type // conversions. If so, it writes // // _cgoBaseNN := &x // _cgoNN := _cgoBaseNN // with type conversions, if any // // to sb, and writes // // _cgoCheckPointer(_cgoBaseNN, true) // // to sbCheck, and returns true. This tells _cgoCheckPointer to check // just the contents of the pointer being passed, not any other part // of the memory allocation. This is run after checkIndex, which looks // for the special case of &a[i], which requires different checks. func (p *Package) checkAddr(sb, sbCheck *bytes.Buffer, arg ast.Expr, i int) bool { // Strip type conversions. px := &arg for { c, ok := (*px).(*ast.CallExpr) if !ok || len(c.Args) != 1 { break } if !p.isType(c.Fun) && !p.isUnsafeData(c.Fun, false) { break } px = &c.Args[0] } if u, ok := (*px).(*ast.UnaryExpr); !ok || u.Op != token.AND { return false } fmt.Fprintf(sb, "_cgoBase%d := %s; ", i, gofmtPos(*px, (*px).Pos())) origX := *px *px = ast.NewIdent(fmt.Sprintf("_cgoBase%d", i)) fmt.Fprintf(sb, "_cgo%d := %s; ", i, gofmtPos(arg, arg.Pos())) *px = origX // Use "0 == 0" to do the right thing in the unlikely event // that "true" is shadowed. fmt.Fprintf(sbCheck, "_cgoCheckPointer(_cgoBase%d, 0 == 0); ", i) return true } // checkSlice checks whether arg has the form x[i:j], possibly inside // type conversions. If so, it writes // // _cgoSliceNN := x[i:j] // _cgoNN := _cgoSliceNN // with type conversions, if any // // to sb, and writes // // _cgoCheckPointer(_cgoSliceNN, true) // // to sbCheck, and returns true. This tells _cgoCheckPointer to check // just the contents of the slice being passed, not any other part // of the memory allocation. func (p *Package) checkSlice(sb, sbCheck *bytes.Buffer, arg ast.Expr, i int) bool { // Strip type conversions. px := &arg for { c, ok := (*px).(*ast.CallExpr) if !ok || len(c.Args) != 1 { break } if !p.isType(c.Fun) && !p.isUnsafeData(c.Fun, false) { break } px = &c.Args[0] } if _, ok := (*px).(*ast.SliceExpr); !ok { return false } fmt.Fprintf(sb, "_cgoSlice%d := %s; ", i, gofmtPos(*px, (*px).Pos())) origX := *px *px = ast.NewIdent(fmt.Sprintf("_cgoSlice%d", i)) fmt.Fprintf(sb, "_cgo%d := %s; ", i, gofmtPos(arg, arg.Pos())) *px = origX // Use 0 == 0 to do the right thing in the unlikely event // that "true" is shadowed. fmt.Fprintf(sbCheck, "_cgoCheckPointer(_cgoSlice%d, 0 == 0); ", i) return true } // checkUnsafeStringData checks for a call to unsafe.StringData. // The result of that call can't contain a pointer so there is // no need to call _cgoCheckPointer. func (p *Package) checkUnsafeStringData(arg ast.Expr) bool { x := arg for { c, ok := x.(*ast.CallExpr) if !ok || len(c.Args) != 1 { break } if p.isUnsafeData(c.Fun, true) { return true } if !p.isType(c.Fun) { break } x = c.Args[0] } return false } // isType reports whether the expression is definitely a type. // This is conservative--it returns false for an unknown identifier. func (p *Package) isType(t ast.Expr) bool { switch t := t.(type) { case *ast.SelectorExpr: id, ok := t.X.(*ast.Ident) if !ok { return false } if id.Name == "unsafe" && t.Sel.Name == "Pointer" { return true } if id.Name == "C" && typedef["_Ctype_"+t.Sel.Name] != nil { return true } return false case *ast.Ident: // TODO: This ignores shadowing. switch t.Name { case "unsafe.Pointer", "bool", "byte", "complex64", "complex128", "error", "float32", "float64", "int", "int8", "int16", "int32", "int64", "rune", "string", "uint", "uint8", "uint16", "uint32", "uint64", "uintptr": return true } if strings.HasPrefix(t.Name, "_Ctype_") { return true } case *ast.ParenExpr: return p.isType(t.X) case *ast.StarExpr: return p.isType(t.X) case *ast.ArrayType, *ast.StructType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.ChanType: return true } return false } // isUnsafeData reports whether the expression is unsafe.StringData // or unsafe.SliceData. We can ignore these when checking for pointers // because they don't change whether or not their argument contains // any Go pointers. If onlyStringData is true we only check for StringData. func (p *Package) isUnsafeData(x ast.Expr, onlyStringData bool) bool { st, ok := x.(*ast.SelectorExpr) if !ok { return false } id, ok := st.X.(*ast.Ident) if !ok { return false } if id.Name != "unsafe" { return false } if !onlyStringData && st.Sel.Name == "SliceData" { return true } return st.Sel.Name == "StringData" } // isVariable reports whether x is a variable, possibly with field references. func (p *Package) isVariable(x ast.Expr) bool { switch x := x.(type) { case *ast.Ident: return true case *ast.SelectorExpr: return p.isVariable(x.X) case *ast.IndexExpr: return true } return false } // rewriteUnsafe returns a version of t with references to unsafe.Pointer // rewritten to use _cgo_unsafe.Pointer instead. func (p *Package) rewriteUnsafe(t ast.Expr) ast.Expr { switch t := t.(type) { case *ast.Ident: // We don't see a SelectorExpr for unsafe.Pointer; // this is created by code in this file. if t.Name == "unsafe.Pointer" { return ast.NewIdent("_cgo_unsafe.Pointer") } case *ast.ArrayType: t1 := p.rewriteUnsafe(t.Elt) if t1 != t.Elt { r := *t r.Elt = t1 return &r } case *ast.StructType: changed := false fields := *t.Fields fields.List = nil for _, f := range t.Fields.List { ft := p.rewriteUnsafe(f.Type) if ft == f.Type { fields.List = append(fields.List, f) } else { fn := *f fn.Type = ft fields.List = append(fields.List, &fn) changed = true } } if changed { r := *t r.Fields = &fields return &r } case *ast.StarExpr: // Pointer type. x1 := p.rewriteUnsafe(t.X) if x1 != t.X { r := *t r.X = x1 return &r } } return t } // 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. In *godefs mode, rewriteRef replaces the names // with full definitions instead of mangled names. func (p *Package) rewriteRef(f *File) { // Keep a list of all the functions, to remove the ones // only used as expressions and avoid generating bridge // code for them. functions := make(map[string]bool) for _, n := range f.Name { if n.Kind == "func" { functions[n.Go] = false } } // 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.IsConst() && r.Name.Const == "" { error_(r.Pos(), "unable to find value of constant C.%s", fixGo(r.Name.Go)) } if r.Name.Kind == "func" { switch r.Context { case ctxCall, ctxCall2: functions[r.Name.Go] = true } } expr := p.rewriteName(f, r, false) if *godefs { // Substitute definition for mangled type name. if r.Name.Type != nil && r.Name.Kind == "type" { expr = r.Name.Type.Go } if id, ok := expr.(*ast.Ident); ok { if t := typedef[id.Name]; t != nil { expr = t.Go } if id.Name == r.Name.Mangle && r.Name.Const != "" { expr = ast.NewIdent(r.Name.Const) } } } // Copy position information from old expr into new expr, // in case expression being replaced is first on line. // See golang.org/issue/6563. pos := (*r.Expr).Pos() if x, ok := expr.(*ast.Ident); ok { expr = &ast.Ident{NamePos: pos, Name: x.Name} } // Change AST, because some later processing depends on it, // and also because -godefs mode still prints the AST. old := *r.Expr *r.Expr = expr // Record source-level edit for cgo output. if !r.Done { // Prepend a space in case the earlier code ends // with '/', which would give us a "//" comment. repl := " " + gofmtPos(expr, old.Pos()) end := fset.Position(old.End()) // Subtract 1 from the column if we are going to // append a close parenthesis. That will set the // correct column for the following characters. sub := 0 if r.Name.Kind != "type" { sub = 1 } if end.Column > sub { repl = fmt.Sprintf("%s /*line :%d:%d*/", repl, end.Line, end.Column-sub) } if r.Name.Kind != "type" { repl = "(" + repl + ")" } f.Edit.Replace(f.offset(old.Pos()), f.offset(old.End()), repl) } } // Remove functions only used as expressions, so their respective // bridge functions are not generated. for name, used := range functions { if !used { delete(f.Name, name) } } } // rewriteName returns the expression used to rewrite a reference. // If addPosition is true, add position info in the ident name. func (p *Package) rewriteName(f *File, r *Ref, addPosition bool) ast.Expr { getNewIdent := ast.NewIdent if addPosition { getNewIdent = func(newName string) *ast.Ident { mangledIdent := ast.NewIdent(newName) if len(newName) == len(r.Name.Go) { return mangledIdent } p := fset.Position((*r.Expr).End()) if p.Column == 0 { return mangledIdent } return ast.NewIdent(fmt.Sprintf("%s /*line :%d:%d*/", newName, p.Line, p.Column)) } } var expr ast.Expr = getNewIdent(r.Name.Mangle) // default switch r.Context { case ctxCall, ctxCall2: if r.Name.Kind != "func" { if r.Name.Kind == "type" { r.Context = ctxType if r.Name.Type == nil { error_(r.Pos(), "invalid conversion to C.%s: undefined C type '%s'", fixGo(r.Name.Go), r.Name.C) } break } error_(r.Pos(), "call of non-function C.%s", fixGo(r.Name.Go)) break } if r.Context == ctxCall2 { if r.Name.Go == "_CMalloc" { error_(r.Pos(), "no two-result form for C.malloc") break } // 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 = getNewIdent(n.Mangle) r.Name = n break } case ctxExpr: switch r.Name.Kind { case "func": if builtinDefs[r.Name.C] != "" { error_(r.Pos(), "use of builtin '%s' not in function call", fixGo(r.Name.C)) } // Function is being used in an expression, to e.g. pass around a C function pointer. // Create a new Name for this Ref which causes the variable to be declared in Go land. fpName := "fp_" + r.Name.Go name := f.Name[fpName] if name == nil { name = &Name{ Go: fpName, C: r.Name.C, Kind: "fpvar", Type: &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("void*"), Go: ast.NewIdent("unsafe.Pointer")}, } p.mangleName(name) f.Name[fpName] = name } r.Name = name // Rewrite into call to _Cgo_ptr to prevent assignments. The _Cgo_ptr // function is defined in out.go and simply returns its argument. See // issue 7757. expr = &ast.CallExpr{ Fun: &ast.Ident{NamePos: (*r.Expr).Pos(), Name: "_Cgo_ptr"}, Args: []ast.Expr{getNewIdent(name.Mangle)}, } case "type": // Okay - might be new(T), T(x), Generic[T], etc. if r.Name.Type == nil { error_(r.Pos(), "expression C.%s: undefined C type '%s'", fixGo(r.Name.Go), r.Name.C) } case "var": expr = &ast.StarExpr{Star: (*r.Expr).Pos(), X: expr} case "macro": expr = &ast.CallExpr{Fun: expr} } case ctxSelector: if r.Name.Kind == "var" { expr = &ast.StarExpr{Star: (*r.Expr).Pos(), X: expr} } else { error_(r.Pos(), "only C variables allowed in selector expression %s", fixGo(r.Name.Go)) } case ctxType: if r.Name.Kind != "type" { error_(r.Pos(), "expression C.%s used as type", fixGo(r.Name.Go)) } else if r.Name.Type == nil { // Use of C.enum_x, C.struct_x or C.union_x without C definition. // GCC won't raise an error when using pointers to such unknown types. error_(r.Pos(), "type C.%s: undefined C type '%s'", fixGo(r.Name.Go), r.Name.C) } default: if r.Name.Kind == "func" { error_(r.Pos(), "must call C.%s", fixGo(r.Name.Go)) } } return expr } // gofmtPos returns the gofmt-formatted string for an AST node, // with a comment setting the position before the node. func gofmtPos(n ast.Expr, pos token.Pos) string { s := gofmtLine(n) p := fset.Position(pos) if p.Column == 0 { return s } return fmt.Sprintf("/*line :%d:%d*/%s", p.Line, p.Column, s) } // checkGCCBaseCmd returns the start of the compiler command line. // It uses $CC if set, or else $GCC, or else the compiler recorded // during the initial build as defaultCC. // defaultCC is defined in zdefaultcc.go, written by cmd/dist. // // The compiler command line is split into arguments on whitespace. Quotes // are understood, so arguments may contain whitespace. // // checkGCCBaseCmd confirms that the compiler exists in PATH, returning // an error if it does not. func checkGCCBaseCmd() ([]string, error) { // Use $CC if set, since that's what the build uses. value := os.Getenv("CC") if value == "" { // Try $GCC if set, since that's what we used to use. value = os.Getenv("GCC") } if value == "" { value = defaultCC(goos, goarch) } args, err := quoted.Split(value) if err != nil { return nil, err } if len(args) == 0 { return nil, errors.New("CC not set and no default found") } if _, err := exec.LookPath(args[0]); err != nil { return nil, fmt.Errorf("C compiler %q not found: %v", args[0], err) } return args[:len(args):len(args)], nil } // gccMachine returns the gcc -m flag to use, either "-m32", "-m64" or "-marm". func (p *Package) gccMachine() []string { switch goarch { case "amd64": if goos == "darwin" { return []string{"-arch", "x86_64", "-m64"} } return []string{"-m64"} case "arm64": if goos == "darwin" { return []string{"-arch", "arm64"} } case "386": return []string{"-m32"} case "arm": return []string{"-marm"} // not thumb case "s390": return []string{"-m31"} case "s390x": return []string{"-m64"} case "mips64", "mips64le": if gomips64 == "hardfloat" { return []string{"-mabi=64", "-mhard-float"} } else if gomips64 == "softfloat" { return []string{"-mabi=64", "-msoft-float"} } case "mips", "mipsle": if gomips == "hardfloat" { return []string{"-mabi=32", "-mfp32", "-mhard-float", "-mno-odd-spreg"} } else if gomips == "softfloat" { return []string{"-mabi=32", "-msoft-float"} } case "loong64": return []string{"-mabi=lp64d"} } return nil } func gccTmp() string { return *objDir + "_cgo_.o" } // gccCmd returns the gcc command line to use for compiling // the input. func (p *Package) gccCmd() []string { c := append(gccBaseCmd, "-w", // no warnings "-Wno-error", // warnings are not errors "-o"+gccTmp(), // write object to tmp "-gdwarf-2", // generate DWARF v2 debugging symbols "-c", // do not link "-xc", // input language is C ) if p.GccIsClang { c = append(c, "-ferror-limit=0", // Apple clang version 1.7 (tags/Apple/clang-77) (based on LLVM 2.9svn) // doesn't have -Wno-unneeded-internal-declaration, so we need yet another // flag to disable the warning. Yes, really good diagnostics, clang. "-Wno-unknown-warning-option", "-Wno-unneeded-internal-declaration", "-Wno-unused-function", "-Qunused-arguments", // Clang embeds prototypes for some builtin functions, // like malloc and calloc, but all size_t parameters are // incorrectly typed unsigned long. We work around that // by disabling the builtin functions (this is safe as // it won't affect the actual compilation of the C code). // See: https://golang.org/issue/6506. "-fno-builtin", ) } c = append(c, p.GccOptions...) c = append(c, p.gccMachine()...) if goos == "aix" { c = append(c, "-maix64") c = append(c, "-mcmodel=large") } // disable LTO so we get an object whose symbols we can read c = append(c, "-fno-lto") 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, nnames int) (d *dwarf.Data, ints []int64, floats []float64, strs []string) { runGcc(stdin, p.gccCmd()) isDebugInts := func(s string) bool { // Some systems use leading _ to denote non-assembly symbols. return s == "__cgodebug_ints" || s == "___cgodebug_ints" } isDebugFloats := func(s string) bool { // Some systems use leading _ to denote non-assembly symbols. return s == "__cgodebug_floats" || s == "___cgodebug_floats" } indexOfDebugStr := func(s string) int { // Some systems use leading _ to denote non-assembly symbols. if strings.HasPrefix(s, "___") { s = s[1:] } if strings.HasPrefix(s, "__cgodebug_str__") { if n, err := strconv.Atoi(s[len("__cgodebug_str__"):]); err == nil { return n } } return -1 } indexOfDebugStrlen := func(s string) int { // Some systems use leading _ to denote non-assembly symbols. if strings.HasPrefix(s, "___") { s = s[1:] } if strings.HasPrefix(s, "__cgodebug_strlen__") { if n, err := strconv.Atoi(s[len("__cgodebug_strlen__"):]); err == nil { return n } } return -1 } strs = make([]string, nnames) strdata := make(map[int]string, nnames) strlens := make(map[int]int, nnames) buildStrings := func() { for n, strlen := range strlens { data := strdata[n] if len(data) <= strlen { fatalf("invalid string literal") } strs[n] = data[:strlen] } } if f, err := macho.Open(gccTmp()); err == nil { defer f.Close() d, err := f.DWARF() if err != nil { fatalf("cannot load DWARF output from %s: %v", gccTmp(), err) } bo := f.ByteOrder if f.Symtab != nil { for i := range f.Symtab.Syms { s := &f.Symtab.Syms[i] switch { case isDebugInts(s.Name): // 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:] ints = make([]int64, len(data)/8) for i := range ints { ints[i] = int64(bo.Uint64(data[i*8:])) } } } } case isDebugFloats(s.Name): // 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:] floats = make([]float64, len(data)/8) for i := range floats { floats[i] = math.Float64frombits(bo.Uint64(data[i*8:])) } } } } default: if n := indexOfDebugStr(s.Name); n != -1 { // 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:] strdata[n] = string(data) } } } break } if n := indexOfDebugStrlen(s.Name); n != -1 { // 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:] strlen := bo.Uint64(data[:8]) if strlen > (1<<(uint(p.IntSize*8)-1) - 1) { // greater than MaxInt? fatalf("string literal too big") } strlens[n] = int(strlen) } } } break } } } buildStrings() } return d, ints, floats, strs } if f, err := elf.Open(gccTmp()); err == nil { defer f.Close() d, err := f.DWARF() if err != nil { fatalf("cannot load DWARF output from %s: %v", gccTmp(), err) } bo := f.ByteOrder symtab, err := f.Symbols() if err == nil { // Check for use of -fsanitize=hwaddress (issue 53285). removeTag := func(v uint64) uint64 { return v } if goarch == "arm64" { for i := range symtab { if symtab[i].Name == "__hwasan_init" { // -fsanitize=hwaddress on ARM // uses the upper byte of a // memory address as a hardware // tag. Remove it so that // we can find the associated // data. removeTag = func(v uint64) uint64 { return v &^ (0xff << (64 - 8)) } break } } } for i := range symtab { s := &symtab[i] switch { case isDebugInts(s.Name): // Found it. Now find data section. if i := int(s.Section); 0 <= i && i < len(f.Sections) { sect := f.Sections[i] val := removeTag(s.Value) if sect.Addr <= val && val < sect.Addr+sect.Size { if sdat, err := sect.Data(); err == nil { data := sdat[val-sect.Addr:] ints = make([]int64, len(data)/8) for i := range ints { ints[i] = int64(bo.Uint64(data[i*8:])) } } } } case isDebugFloats(s.Name): // Found it. Now find data section. if i := int(s.Section); 0 <= i && i < len(f.Sections) { sect := f.Sections[i] val := removeTag(s.Value) if sect.Addr <= val && val < sect.Addr+sect.Size { if sdat, err := sect.Data(); err == nil { data := sdat[val-sect.Addr:] floats = make([]float64, len(data)/8) for i := range floats { floats[i] = math.Float64frombits(bo.Uint64(data[i*8:])) } } } } default: if n := indexOfDebugStr(s.Name); n != -1 { // Found it. Now find data section. if i := int(s.Section); 0 <= i && i < len(f.Sections) { sect := f.Sections[i] val := removeTag(s.Value) if sect.Addr <= val && val < sect.Addr+sect.Size { if sdat, err := sect.Data(); err == nil { data := sdat[val-sect.Addr:] strdata[n] = string(data) } } } break } if n := indexOfDebugStrlen(s.Name); n != -1 { // Found it. Now find data section. if i := int(s.Section); 0 <= i && i < len(f.Sections) { sect := f.Sections[i] val := removeTag(s.Value) if sect.Addr <= val && val < sect.Addr+sect.Size { if sdat, err := sect.Data(); err == nil { data := sdat[val-sect.Addr:] strlen := bo.Uint64(data[:8]) if strlen > (1<<(uint(p.IntSize*8)-1) - 1) { // greater than MaxInt? fatalf("string literal too big") } strlens[n] = int(strlen) } } } break } } } buildStrings() } return d, ints, floats, strs } if f, err := pe.Open(gccTmp()); err == nil { defer f.Close() d, err := f.DWARF() if err != nil { fatalf("cannot load DWARF output from %s: %v", gccTmp(), err) } bo := binary.LittleEndian for _, s := range f.Symbols { switch { case isDebugInts(s.Name): if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) { sect := f.Sections[i] if s.Value < sect.Size { if sdat, err := sect.Data(); err == nil { data := sdat[s.Value:] ints = make([]int64, len(data)/8) for i := range ints { ints[i] = int64(bo.Uint64(data[i*8:])) } } } } case isDebugFloats(s.Name): if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) { sect := f.Sections[i] if s.Value < sect.Size { if sdat, err := sect.Data(); err == nil { data := sdat[s.Value:] floats = make([]float64, len(data)/8) for i := range floats { floats[i] = math.Float64frombits(bo.Uint64(data[i*8:])) } } } } default: if n := indexOfDebugStr(s.Name); n != -1 { if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) { sect := f.Sections[i] if s.Value < sect.Size { if sdat, err := sect.Data(); err == nil { data := sdat[s.Value:] strdata[n] = string(data) } } } break } if n := indexOfDebugStrlen(s.Name); n != -1 { if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) { sect := f.Sections[i] if s.Value < sect.Size { if sdat, err := sect.Data(); err == nil { data := sdat[s.Value:] strlen := bo.Uint64(data[:8]) if strlen > (1<<(uint(p.IntSize*8)-1) - 1) { // greater than MaxInt? fatalf("string literal too big") } strlens[n] = int(strlen) } } } break } } } buildStrings() return d, ints, floats, strs } if f, err := xcoff.Open(gccTmp()); err == nil { defer f.Close() d, err := f.DWARF() if err != nil { fatalf("cannot load DWARF output from %s: %v", gccTmp(), err) } bo := binary.BigEndian for _, s := range f.Symbols { switch { case isDebugInts(s.Name): if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) { sect := f.Sections[i] if s.Value < sect.Size { if sdat, err := sect.Data(); err == nil { data := sdat[s.Value:] ints = make([]int64, len(data)/8) for i := range ints { ints[i] = int64(bo.Uint64(data[i*8:])) } } } } case isDebugFloats(s.Name): if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) { sect := f.Sections[i] if s.Value < sect.Size { if sdat, err := sect.Data(); err == nil { data := sdat[s.Value:] floats = make([]float64, len(data)/8) for i := range floats { floats[i] = math.Float64frombits(bo.Uint64(data[i*8:])) } } } } default: if n := indexOfDebugStr(s.Name); n != -1 { if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) { sect := f.Sections[i] if s.Value < sect.Size { if sdat, err := sect.Data(); err == nil { data := sdat[s.Value:] strdata[n] = string(data) } } } break } if n := indexOfDebugStrlen(s.Name); n != -1 { if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) { sect := f.Sections[i] if s.Value < sect.Size { if sdat, err := sect.Data(); err == nil { data := sdat[s.Value:] strlen := bo.Uint64(data[:8]) if strlen > (1<<(uint(p.IntSize*8)-1) - 1) { // greater than MaxInt? fatalf("string literal too big") } strlens[n] = int(strlen) } } } break } } } buildStrings() return d, ints, floats, strs } fatalf("cannot parse gcc output %s as ELF, Mach-O, PE, XCOFF 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 := append(gccBaseCmd, "-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, extraArgs ...string) string { // TODO(rsc): require failure args := p.gccCmd() // Optimization options can confuse the error messages; remove them. nargs := make([]string, 0, len(args)+len(extraArgs)) for _, arg := range args { if !strings.HasPrefix(arg, "-O") { nargs = append(nargs, arg) } } // Force -O0 optimization and append extra arguments, but keep the // trailing "-" at the end. li := len(nargs) - 1 last := nargs[li] nargs[li] = "-O0" nargs = append(nargs, extraArgs...) nargs = append(nargs, last) if *debugGcc { fmt.Fprintf(os.Stderr, "$ %s < 0 { dtype := c.ptrKeys[0] dtypeKey := dtype.String() c.ptrKeys = c.ptrKeys[1:] ptrs := c.ptrs[dtypeKey] delete(c.ptrs, dtypeKey) // Note Type might invalidate c.ptrs[dtypeKey]. t := c.Type(dtype, pos) for _, ptr := range ptrs { ptr.Go.(*ast.StarExpr).X = t.Go ptr.C.Set("%s*", t.C) } } } // 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, pos token.Pos) *Type { return c.loadType(dtype, pos, "") } // loadType recursively loads the requested dtype and its dependency graph. func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Type { // Always recompute bad pointer typedefs, as the set of such // typedefs changes as we see more types. checkCache := true if dtt, ok := dtype.(*dwarf.TypedefType); ok && c.badPointerTypedef(dtt) { checkCache = false } // The cache key should be relative to its parent. // See issue https://golang.org/issue/31891 key := parent + " > " + dtype.String() if checkCache { if t, ok := c.m[key]; ok { if t.Go == nil { fatalf("%s: type conversion loop at %s", lineno(pos), dtype) } return t } } t := new(Type) t.Size = dtype.Size() // note: wrong for array of pointers, corrected below t.Align = -1 t.C = &TypeRepr{Repr: dtype.Common().Name} c.m[key] = t switch dt := dtype.(type) { default: fatalf("%s: unexpected type: %s", lineno(pos), dtype) case *dwarf.AddrType: if t.Size != c.ptrSize { fatalf("%s: unexpected: %d-byte address type - %s", lineno(pos), 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 } count := dt.Count if count == -1 { // Indicates flexible array member, which Go doesn't support. // Translate to zero-length array instead. count = 0 } sub := c.Type(dt.Type, pos) t.Align = sub.Align t.Go = &ast.ArrayType{ Len: c.intExpr(count), Elt: sub.Go, } // Recalculate t.Size now that we know sub.Size. t.Size = count * sub.Size t.C.Set("__typeof__(%s[%d])", sub.C, dt.Count) case *dwarf.BoolType: t.Go = c.bool t.Align = 1 case *dwarf.CharType: if t.Size != 1 { fatalf("%s: unexpected: %d-byte char type - %s", lineno(pos), 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("%s: unexpected: %d-byte enum type - %s", lineno(pos), 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("%s: unexpected: %d-byte float type - %s", lineno(pos), 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("%s: unexpected: %d-byte complex type - %s", lineno(pos), t.Size, dtype) case 8: t.Go = c.complex64 case 16: t.Go = c.complex128 } if t.Align = t.Size / 2; 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("%s: unexpected: %d-bit int type - %s", lineno(pos), dt.BitSize, dtype) } switch t.Size { default: fatalf("%s: unexpected: %d-byte int type - %s", lineno(pos), 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 case 16: t.Go = &ast.ArrayType{ Len: c.intExpr(t.Size), Elt: c.uint8, } } if t.Align = t.Size; t.Align >= c.ptrSize { t.Align = c.ptrSize } case *dwarf.PtrType: // Clang doesn't emit DW_AT_byte_size for pointer types. if t.Size != c.ptrSize && t.Size != -1 { fatalf("%s: unexpected: %d-byte pointer type - %s", lineno(pos), t.Size, dtype) } t.Size = c.ptrSize t.Align = c.ptrSize if _, ok := base(dt.Type).(*dwarf.VoidType); ok { t.Go = c.goVoidPtr t.C.Set("void*") dq := dt.Type for { if d, ok := dq.(*dwarf.QualType); ok { t.C.Set(d.Qual + " " + t.C.String()) dq = d.Type } else { break } } break } // Placeholder initialization; completed in FinishType. t.Go = &ast.StarExpr{} t.C.Set("*") key := dt.Type.String() if _, ok := c.ptrs[key]; !ok { c.ptrKeys = append(c.ptrKeys, dt.Type) } c.ptrs[key] = append(c.ptrs[key], t) case *dwarf.QualType: t1 := c.Type(dt.Type, pos) t.Size = t1.Size t.Align = t1.Align t.Go = t1.Go if unionWithPointer[t1.Go] { unionWithPointer[t.Go] = true } t.EnumValues = nil t.Typedef = "" t.C.Set("%s "+dt.Qual, t1.C) 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 dt.ByteSize < 0 && tag == "" { // opaque unnamed struct - should not be possible break } if tag == "" { tag = anonymousStructTag[dt] if tag == "" { tag = "__" + strconv.Itoa(tagGen) tagGen++ anonymousStructTag[dt] = tag } } 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 goIdent[name.Name] = name if dt.ByteSize < 0 { // Don't override old type if _, ok := typedef[name.Name]; ok { break } // Size calculation in c.Struct/c.Opaque will die with size=-1 (unknown), // so execute the basic things that the struct case would do // other than try to determine a Go representation. tt := *t tt.C = &TypeRepr{"%s %s", []interface{}{dt.Kind, tag}} // We don't know what the representation of this struct is, so don't let // anyone allocate one on the Go side. As a side effect of this annotation, // pointers to this type will not be considered pointers in Go. They won't // get writebarrier-ed or adjusted during a stack copy. This should handle // all the cases badPointerTypedef used to handle, but hopefully will // continue to work going forward without any more need for cgo changes. tt.Go = c.Ident(incomplete) typedef[name.Name] = &tt break } switch dt.Kind { case "class", "union": t.Go = c.Opaque(t.Size) if c.dwarfHasPointer(dt, pos) { unionWithPointer[t.Go] = true } if t.C.Empty() { t.C.Set("__typeof__(unsigned char[%d])", t.Size) } t.Align = 1 // TODO: should probably base this on field alignment. typedef[name.Name] = t case "struct": g, csyntax, align := c.Struct(dt, pos) if t.C.Empty() { t.C.Set(csyntax) } t.Align = align tt := *t if tag != "" { tt.C = &TypeRepr{"struct %s", []interface{}{tag}} } tt.Go = g if c.incompleteStructs[tag] { tt.Go = c.Ident(incomplete) } typedef[name.Name] = &tt } 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 } if dt.Name == "_GoBytes_" { // Special C name for Go []byte type. // Knows slice layout used by compilers: pointer, length, cap. t.Go = c.Ident("[]byte") t.Size = c.ptrSize + 4 + 4 t.Align = c.ptrSize break } name := c.Ident("_Ctype_" + dt.Name) goIdent[name.Name] = name akey := "" if c.anonymousStructTypedef(dt) { // only load type recursively for typedefs of anonymous // structs, see issues 37479 and 37621. akey = key } sub := c.loadType(dt.Type, pos, akey) if c.badPointerTypedef(dt) { // Treat this typedef as a uintptr. s := *sub s.Go = c.uintptr s.BadPointer = true sub = &s // Make sure we update any previously computed type. if oldType := typedef[name.Name]; oldType != nil { oldType.Go = sub.Go oldType.BadPointer = true } } if c.badVoidPointerTypedef(dt) { // Treat this typedef as a pointer to a _cgopackage.Incomplete. s := *sub s.Go = c.Ident("*" + incomplete) sub = &s // Make sure we update any previously computed type. if oldType := typedef[name.Name]; oldType != nil { oldType.Go = sub.Go } } // Check for non-pointer "struct {...}; typedef struct *" // typedefs that should be marked Incomplete. if ptr, ok := dt.Type.(*dwarf.PtrType); ok { if strct, ok := ptr.Type.(*dwarf.StructType); ok { if c.badStructPointerTypedef(dt.Name, strct) { c.incompleteStructs[strct.StructName] = true // Make sure we update any previously computed type. name := "_Ctype_struct_" + strct.StructName if oldType := typedef[name]; oldType != nil { oldType.Go = c.Ident(incomplete) } } } } t.Go = name t.BadPointer = sub.BadPointer if unionWithPointer[sub.Go] { unionWithPointer[t.Go] = true } t.Size = sub.Size t.Align = sub.Align oldType := typedef[name.Name] if oldType == nil { tt := *t tt.Go = sub.Go tt.BadPointer = sub.BadPointer typedef[name.Name] = &tt } // If sub.Go.Name is "_Ctype_struct_foo" or "_Ctype_union_foo" or "_Ctype_class_foo", // use that as the Go form for this typedef too, so that the typedef will be interchangeable // with the base type. // In -godefs mode, do this for all typedefs. if isStructUnionClass(sub.Go) || *godefs { t.Go = sub.Go if isStructUnionClass(sub.Go) { // Use the typedef name for C code. typedef[sub.Go.(*ast.Ident).Name].C = t.C } // If we've seen this typedef before, and it // was an anonymous struct/union/class before // too, use the old definition. // TODO: it would be safer to only do this if // we verify that the types are the same. if oldType != nil && isStructUnionClass(oldType.Go) { t.Go = oldType.Go } } case *dwarf.UcharType: if t.Size != 1 { fatalf("%s: unexpected: %d-byte uchar type - %s", lineno(pos), t.Size, dtype) } t.Go = c.uint8 t.Align = 1 case *dwarf.UintType: if dt.BitSize > 0 { fatalf("%s: unexpected: %d-bit uint type - %s", lineno(pos), dt.BitSize, dtype) } switch t.Size { default: fatalf("%s: unexpected: %d-byte uint type - %s", lineno(pos), 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 16: t.Go = &ast.ArrayType{ Len: c.intExpr(t.Size), Elt: c.uint8, } } if t.Align = t.Size; t.Align >= c.ptrSize { t.Align = c.ptrSize } case *dwarf.VoidType: t.Go = c.goVoid t.C.Set("void") t.Align = 1 } switch dtype.(type) { case *dwarf.AddrType, *dwarf.BoolType, *dwarf.CharType, *dwarf.ComplexType, *dwarf.IntType, *dwarf.FloatType, *dwarf.UcharType, *dwarf.UintType: s := dtype.Common().Name if s != "" { if ss, ok := dwarfToName[s]; ok { s = ss } s = strings.Replace(s, " ", "", -1) name := c.Ident("_Ctype_" + s) tt := *t typedef[name.Name] = &tt if !*godefs { t.Go = name } } } if t.Size < 0 { // Unsized types are [0]byte, unless they're typedefs of other types // or structs with tags. // if so, use the name we've already defined. t.Size = 0 switch dt := dtype.(type) { case *dwarf.TypedefType: // ok case *dwarf.StructType: if dt.StructName != "" { break } t.Go = c.Opaque(0) default: t.Go = c.Opaque(0) } if t.C.Empty() { t.C.Set("void") } } if t.C.Empty() { fatalf("%s: internal error: did not create C name for %s", lineno(pos), dtype) } return t } // isStructUnionClass reports whether the type described by the Go syntax x // is a struct, union, or class with a tag. func isStructUnionClass(x ast.Expr) bool { id, ok := x.(*ast.Ident) if !ok { return false } name := id.Name return strings.HasPrefix(name, "_Ctype_struct_") || strings.HasPrefix(name, "_Ctype_union_") || strings.HasPrefix(name, "_Ctype_class_") } // 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, pos token.Pos) *Type { t := c.Type(unqual(dtype), pos) 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 { break } // ...or the typedef is one in which we expect bad pointers. // It will be a uintptr instead of *X. if c.baseBadPointerTypedef(dt) { break } t = c.Type(ptr, pos) if t == nil { return nil } // For a struct/union/class, remember the C spelling, // in case it has __attribute__((unavailable)). // See issue 2888. if isStructUnionClass(t.Go) { t.Typedef = dt.Name } } } 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, pos token.Pos) *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, pos) gp[i] = &ast.Field{Type: p[i].Go} } var r *Type var gr []*ast.Field if _, ok := base(dtype.ReturnType).(*dwarf.VoidType); ok { gr = []*ast.Field{{Type: c.goVoid}} } else if dtype.ReturnType != nil { r = c.Type(unqual(dtype.ReturnType), pos) gr = []*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.FormatInt(n, 10), } } // Add padding of given size to fld. func (c *typeConv) pad(fld []*ast.Field, sizes []int64, size int64) ([]*ast.Field, []int64) { n := len(fld) fld = fld[0 : n+1] fld[n] = &ast.Field{Names: []*ast.Ident{c.Ident("_")}, Type: c.Opaque(size)} sizes = sizes[0 : n+1] sizes[n] = size return fld, sizes } // Struct conversion: return Go and (gc) C syntax for type. func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.StructType, csyntax string, align int64) { // Minimum alignment for a struct is 1 byte. align = 1 var buf strings.Builder buf.WriteString("struct {") fld := make([]*ast.Field, 0, 2*len(dt.Field)+1) // enough for padding around every field sizes := make([]int64, 0, 2*len(dt.Field)+1) 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 } if !*godefs { for cid, goid := range ident { if token.Lookup(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 } } } anon := 0 for _, f := range dt.Field { name := f.Name ft := f.Type // In godefs mode, if this field is a C11 // anonymous union then treat the first field in the // union as the field in the struct. This handles // cases like the glibc file; see // issue 6677. if *godefs { if st, ok := f.Type.(*dwarf.StructType); ok && name == "" && st.Kind == "union" && len(st.Field) > 0 && !used[st.Field[0].Name] { name = st.Field[0].Name ident[name] = name ft = st.Field[0].Type } } // TODO: Handle fields that are anonymous structs by // promoting the fields of the inner struct. t := c.Type(ft, pos) tgo := t.Go size := t.Size talign := t.Align if f.BitOffset > 0 || f.BitSize > 0 { // The layout of bitfields is implementation defined, // so we don't know how they correspond to Go fields // even if they are aligned at byte boundaries. continue } if talign > 0 && f.ByteOffset%talign != 0 { // Drop misaligned fields, the same way we drop integer bit fields. // The goal is to make available what can be made available. // Otherwise one bad and unneeded field in an otherwise okay struct // makes the whole program not compile. Much of the time these // structs are in system headers that cannot be corrected. continue } // Round off up to talign, assumed to be a power of 2. off = (off + talign - 1) &^ (talign - 1) if f.ByteOffset > off { fld, sizes = c.pad(fld, sizes, f.ByteOffset-off) off = f.ByteOffset } if f.ByteOffset < off { // Drop a packed field that we can't represent. continue } n := len(fld) fld = fld[0 : n+1] if name == "" { name = fmt.Sprintf("anon%d", anon) anon++ ident[name] = name } fld[n] = &ast.Field{Names: []*ast.Ident{c.Ident(ident[name])}, Type: tgo} sizes = sizes[0 : n+1] sizes[n] = size off += size buf.WriteString(t.C.String()) buf.WriteString(" ") buf.WriteString(name) buf.WriteString("; ") if talign > align { align = talign } } if off < dt.ByteSize { fld, sizes = c.pad(fld, sizes, dt.ByteSize-off) off = dt.ByteSize } // If the last field in a non-zero-sized struct is zero-sized // the compiler is going to pad it by one (see issue 9401). // We can't permit that, because then the size of the Go // struct will not be the same as the size of the C struct. // Our only option in such a case is to remove the field, // which means that it cannot be referenced from Go. for off > 0 && sizes[len(sizes)-1] == 0 { n := len(sizes) fld = fld[0 : n-1] sizes = sizes[0 : n-1] } if off != dt.ByteSize { fatalf("%s: struct size calculation error off=%d bytesize=%d", lineno(pos), off, dt.ByteSize) } buf.WriteString("}") csyntax = buf.String() if *godefs { godefsFields(fld) } expr = &ast.StructType{Fields: &ast.FieldList{List: fld}} return } // dwarfHasPointer reports whether the DWARF type dt contains a pointer. func (c *typeConv) dwarfHasPointer(dt dwarf.Type, pos token.Pos) bool { switch dt := dt.(type) { default: fatalf("%s: unexpected type: %s", lineno(pos), dt) return false case *dwarf.AddrType, *dwarf.BoolType, *dwarf.CharType, *dwarf.EnumType, *dwarf.FloatType, *dwarf.ComplexType, *dwarf.FuncType, *dwarf.IntType, *dwarf.UcharType, *dwarf.UintType, *dwarf.VoidType: return false case *dwarf.ArrayType: return c.dwarfHasPointer(dt.Type, pos) case *dwarf.PtrType: return true case *dwarf.QualType: return c.dwarfHasPointer(dt.Type, pos) case *dwarf.StructType: for _, f := range dt.Field { if c.dwarfHasPointer(f.Type, pos) { return true } } return false case *dwarf.TypedefType: if dt.Name == "_GoString_" || dt.Name == "_GoBytes_" { return true } return c.dwarfHasPointer(dt.Type, pos) } } func upper(s string) string { if s == "" { return "" } r, size := utf8.DecodeRuneInString(s) if r == '_' { return "X" + s } return string(unicode.ToUpper(r)) + s[size:] } // godefsFields rewrites field names for use in Go or C definitions. // It strips leading common prefixes (like tv_ in tv_sec, tv_usec) // converts names to upper case, and rewrites _ into Pad_godefs_n, // so that all fields are exported. func godefsFields(fld []*ast.Field) { prefix := fieldPrefix(fld) // Issue 48396: check for duplicate field names. if prefix != "" { names := make(map[string]bool) fldLoop: for _, f := range fld { for _, n := range f.Names { name := n.Name if name == "_" { continue } if name != prefix { name = strings.TrimPrefix(n.Name, prefix) } name = upper(name) if names[name] { // Field name conflict: don't remove prefix. prefix = "" break fldLoop } names[name] = true } } } npad := 0 for _, f := range fld { for _, n := range f.Names { if n.Name != prefix { n.Name = strings.TrimPrefix(n.Name, prefix) } if n.Name == "_" { // Use exported name instead. n.Name = "Pad_cgo_" + strconv.Itoa(npad) npad++ } n.Name = upper(n.Name) } } } // fieldPrefix returns the prefix that should be removed from all the // field names when generating the C or Go code. For generated // C, we leave the names as is (tv_sec, tv_usec), since that's what // people are used to seeing in C. For generated Go code, such as // package syscall's data structures, we drop a common prefix // (so sec, usec, which will get turned into Sec, Usec for exporting). func fieldPrefix(fld []*ast.Field) string { prefix := "" for _, f := range fld { for _, n := range f.Names { // Ignore field names that don't have the prefix we're // looking for. It is common in C headers to have fields // named, say, _pad in an otherwise prefixed header. // If the struct has 3 fields tv_sec, tv_usec, _pad1, then we // still want to remove the tv_ prefix. // The check for "orig_" here handles orig_eax in the // x86 ptrace register sets, which otherwise have all fields // with reg_ prefixes. if strings.HasPrefix(n.Name, "orig_") || strings.HasPrefix(n.Name, "_") { continue } i := strings.Index(n.Name, "_") if i < 0 { continue } if prefix == "" { prefix = n.Name[:i+1] } else if prefix != n.Name[:i+1] { return "" } } } return prefix } // anonymousStructTypedef reports whether dt is a C typedef for an anonymous // struct. func (c *typeConv) anonymousStructTypedef(dt *dwarf.TypedefType) bool { st, ok := dt.Type.(*dwarf.StructType) return ok && st.StructName == "" } // badPointerTypedef reports whether dt is a C typedef that should not be // considered a pointer in Go. A typedef is bad if C code sometimes stores // non-pointers in this type. // TODO: Currently our best solution is to find these manually and list them as // they come up. A better solution is desired. // Note: DEPRECATED. There is now a better solution. Search for incomplete in this file. func (c *typeConv) badPointerTypedef(dt *dwarf.TypedefType) bool { if c.badCFType(dt) { return true } if c.badJNI(dt) { return true } if c.badEGLType(dt) { return true } return false } // badVoidPointerTypedef is like badPointerTypeDef, but for "void *" typedefs that should be _cgopackage.Incomplete. func (c *typeConv) badVoidPointerTypedef(dt *dwarf.TypedefType) bool { // Match the Windows HANDLE type (#42018). if goos != "windows" || dt.Name != "HANDLE" { return false } // Check that the typedef is "typedef void *". if ptr, ok := dt.Type.(*dwarf.PtrType); ok { if _, ok := ptr.Type.(*dwarf.VoidType); ok { return true } } return false } // badStructPointerTypedef is like badVoidPointerTypedef but for structs. func (c *typeConv) badStructPointerTypedef(name string, dt *dwarf.StructType) bool { // Windows handle types can all potentially contain non-pointers. // badVoidPointerTypedef handles the "void *" HANDLE type, but other // handles are defined as // // struct __{int unused;}; typedef struct __ *name; // // by the DECLARE_HANDLE macro in STRICT mode. The macro is declared in // the Windows ntdef.h header, // // https://github.com/tpn/winsdk-10/blob/master/Include/10.0.16299.0/shared/ntdef.h#L779 if goos != "windows" { return false } if len(dt.Field) != 1 { return false } if dt.StructName != name+"__" { return false } if f := dt.Field[0]; f.Name != "unused" || f.Type.Common().Name != "int" { return false } return true } // baseBadPointerTypedef reports whether the base of a chain of typedefs is a bad typedef // as badPointerTypedef reports. func (c *typeConv) baseBadPointerTypedef(dt *dwarf.TypedefType) bool { for { if t, ok := dt.Type.(*dwarf.TypedefType); ok { dt = t continue } break } return c.badPointerTypedef(dt) } func (c *typeConv) badCFType(dt *dwarf.TypedefType) bool { // The real bad types are CFNumberRef and CFDateRef. // Sometimes non-pointers are stored in these types. // CFTypeRef is a supertype of those, so it can have bad pointers in it as well. // We return true for the other *Ref types just so casting between them is easier. // We identify the correct set of types as those ending in Ref and for which // there exists a corresponding GetTypeID function. // See comment below for details about the bad pointers. if goos != "darwin" && goos != "ios" { return false } s := dt.Name if !strings.HasSuffix(s, "Ref") { return false } s = s[:len(s)-3] if s == "CFType" { return true } if c.getTypeIDs[s] { return true } if i := strings.Index(s, "Mutable"); i >= 0 && c.getTypeIDs[s[:i]+s[i+7:]] { // Mutable and immutable variants share a type ID. return true } return false } // Comment from Darwin's CFInternal.h /* // Tagged pointer support // Low-bit set means tagged object, next 3 bits (currently) // define the tagged object class, next 4 bits are for type // information for the specific tagged object class. Thus, // the low byte is for type info, and the rest of a pointer // (32 or 64-bit) is for payload, whatever the tagged class. // // Note that the specific integers used to identify the // specific tagged classes can and will change from release // to release (that's why this stuff is in CF*Internal*.h), // as can the definition of type info vs payload above. // #if __LP64__ #define CF_IS_TAGGED_OBJ(PTR) ((uintptr_t)(PTR) & 0x1) #define CF_TAGGED_OBJ_TYPE(PTR) ((uintptr_t)(PTR) & 0xF) #else #define CF_IS_TAGGED_OBJ(PTR) 0 #define CF_TAGGED_OBJ_TYPE(PTR) 0 #endif enum { kCFTaggedObjectID_Invalid = 0, kCFTaggedObjectID_Atom = (0 << 1) + 1, kCFTaggedObjectID_Undefined3 = (1 << 1) + 1, kCFTaggedObjectID_Undefined2 = (2 << 1) + 1, kCFTaggedObjectID_Integer = (3 << 1) + 1, kCFTaggedObjectID_DateTS = (4 << 1) + 1, kCFTaggedObjectID_ManagedObjectID = (5 << 1) + 1, // Core Data kCFTaggedObjectID_Date = (6 << 1) + 1, kCFTaggedObjectID_Undefined7 = (7 << 1) + 1, }; */ func (c *typeConv) badJNI(dt *dwarf.TypedefType) bool { // In Dalvik and ART, the jobject type in the JNI interface of the JVM has the // property that it is sometimes (always?) a small integer instead of a real pointer. // Note: although only the android JVMs are bad in this respect, we declare the JNI types // bad regardless of platform, so the same Go code compiles on both android and non-android. if parent, ok := jniTypes[dt.Name]; ok { // Try to make sure we're talking about a JNI type, not just some random user's // type that happens to use the same name. // C doesn't have the notion of a package, so it's hard to be certain. // Walk up to jobject, checking each typedef on the way. w := dt for parent != "" { t, ok := w.Type.(*dwarf.TypedefType) if !ok || t.Name != parent { return false } w = t parent, ok = jniTypes[w.Name] if !ok { return false } } // Check that the typedef is either: // 1: // struct _jobject; // typedef struct _jobject *jobject; // 2: (in NDK16 in C++) // class _jobject {}; // typedef _jobject* jobject; // 3: (in NDK16 in C) // typedef void* jobject; if ptr, ok := w.Type.(*dwarf.PtrType); ok { switch v := ptr.Type.(type) { case *dwarf.VoidType: return true case *dwarf.StructType: if v.StructName == "_jobject" && len(v.Field) == 0 { switch v.Kind { case "struct": if v.Incomplete { return true } case "class": if !v.Incomplete { return true } } } } } } return false } func (c *typeConv) badEGLType(dt *dwarf.TypedefType) bool { if dt.Name != "EGLDisplay" && dt.Name != "EGLConfig" { return false } // Check that the typedef is "typedef void *". if ptr, ok := dt.Type.(*dwarf.PtrType); ok { if _, ok := ptr.Type.(*dwarf.VoidType); ok { return true } } return false } // jniTypes maps from JNI types that we want to be uintptrs, to the underlying type to which // they are mapped. The base "jobject" maps to the empty string. var jniTypes = map[string]string{ "jobject": "", "jclass": "jobject", "jthrowable": "jobject", "jstring": "jobject", "jarray": "jobject", "jbooleanArray": "jarray", "jbyteArray": "jarray", "jcharArray": "jarray", "jshortArray": "jarray", "jintArray": "jarray", "jlongArray": "jarray", "jfloatArray": "jarray", "jdoubleArray": "jarray", "jobjectArray": "jarray", "jweak": "jobject", }