mirror of
https://github.com/golang/go.git
synced 2025-05-28 02:41:30 +00:00
If the aligned offset isn't sufficient for the field offset, we were padding based on the aligned offset. We need to pad based on the original offset instead. Also set the Go alignment correctly for int128. We were defaulting to the maximum alignment, but since we translate int128 into an array of uint8 the correct Go alignment is 1. For #69086 Fixes #69219 Change-Id: I23ce583335c81beac2ac51f7f9336ac97ccebf09 Reviewed-on: https://go-review.googlesource.com/c/go/+/608815 Reviewed-by: Damien Neil <dneil@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Cherry Mui <cherryyz@google.com> Auto-Submit: Ian Lance Taylor <iant@golang.org> (cherry picked from commit c2098929056481d0dc09f5f42b8959f4db8878f2) Reviewed-on: https://go-review.googlesource.com/c/go/+/611296 Reviewed-by: Ian Lance Taylor <iant@google.com> Auto-Submit: Ian Lance Taylor <iant@google.com> Commit-Queue: Ian Lance Taylor <iant@google.com>
3546 lines
97 KiB
Go
3546 lines
97 KiB
Go
// 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) <function name>
|
|
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.<name>", 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(gofmt(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,
|
|
gofmt(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 builtinDefs[r.Name.Go] != "" {
|
|
error_(r.Pos(), "no two-result form for C.%s", r.Name.Go)
|
|
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 := gofmt(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 <<EOF\n", strings.Join(nargs, " "))
|
|
os.Stderr.Write(stdin)
|
|
fmt.Fprint(os.Stderr, "EOF\n")
|
|
}
|
|
stdout, stderr, _ := run(stdin, nargs)
|
|
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[string]*Type
|
|
|
|
// Map from types to incomplete pointers to those types.
|
|
ptrs map[string][]*Type
|
|
// Keys of ptrs in insertion order (deterministic worklist)
|
|
// ptrKeys contains exactly the keys in ptrs.
|
|
ptrKeys []dwarf.Type
|
|
|
|
// Type names X for which there exists an XGetTypeID function with type func() CFTypeID.
|
|
getTypeIDs map[string]bool
|
|
|
|
// incompleteStructs contains C structs that should be marked Incomplete.
|
|
incompleteStructs map[string]bool
|
|
|
|
// 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
|
|
string ast.Expr
|
|
goVoid ast.Expr // _Ctype_void, denotes C's void
|
|
goVoidPtr ast.Expr // unsafe.Pointer or *byte
|
|
|
|
ptrSize int64
|
|
intSize int64
|
|
}
|
|
|
|
var tagGen int
|
|
var typedef = make(map[string]*Type)
|
|
var goIdent = make(map[string]*ast.Ident)
|
|
|
|
// unionWithPointer is true for a Go type that represents a C union (or class)
|
|
// that may contain a pointer. This is used for cgo pointer checking.
|
|
var unionWithPointer = make(map[ast.Expr]bool)
|
|
|
|
// anonymousStructTag provides a consistent tag for an anonymous struct.
|
|
// The same dwarf.StructType pointer will always get the same tag.
|
|
var anonymousStructTag = make(map[*dwarf.StructType]string)
|
|
|
|
func (c *typeConv) Init(ptrSize, intSize int64) {
|
|
c.ptrSize = ptrSize
|
|
c.intSize = intSize
|
|
c.m = make(map[string]*Type)
|
|
c.ptrs = make(map[string][]*Type)
|
|
c.getTypeIDs = make(map[string]bool)
|
|
c.incompleteStructs = make(map[string]bool)
|
|
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.void = c.Ident("void")
|
|
c.string = c.Ident("string")
|
|
c.goVoid = c.Ident("_Ctype_void")
|
|
|
|
// Normally cgo translates void* to unsafe.Pointer,
|
|
// but for historical reasons -godefs uses *byte instead.
|
|
if *godefs {
|
|
c.goVoidPtr = &ast.StarExpr{X: c.byte}
|
|
} else {
|
|
c.goVoidPtr = c.Ident("unsafe.Pointer")
|
|
}
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// unqual strips away qualifiers from a DWARF type.
|
|
// In general we don't care about top-level qualifiers.
|
|
func unqual(dt dwarf.Type) dwarf.Type {
|
|
for {
|
|
if d, ok := dt.(*dwarf.QualType); ok {
|
|
dt = d.Type
|
|
} else {
|
|
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",
|
|
"unsigned short": "ushort", // Used by Clang; issue 13129.
|
|
"short int": "short",
|
|
"long long int": "longlong",
|
|
"long long unsigned int": "ulonglong",
|
|
"signed char": "schar",
|
|
"unsigned char": "uchar",
|
|
"unsigned long": "ulong", // Used by Clang 14; issue 53013.
|
|
"unsigned long long": "ulonglong", // Used by Clang 14; issue 53013.
|
|
}
|
|
|
|
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 reports whether 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
|
|
}
|
|
|
|
// FinishType completes any outstanding type mapping work.
|
|
// In particular, it resolves incomplete pointer types.
|
|
func (c *typeConv) FinishType(pos token.Pos) {
|
|
// Completing one pointer type might produce more to complete.
|
|
// Keep looping until they're all done.
|
|
for len(c.ptrKeys) > 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)
|
|
}
|
|
|
|
if t.Align = t.Size; t.Align >= c.ptrSize {
|
|
t.Align = c.ptrSize
|
|
}
|
|
|
|
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,
|
|
}
|
|
// t.Align is the alignment of the Go type.
|
|
t.Align = 1
|
|
}
|
|
|
|
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("<incomplete>*")
|
|
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 <tag>{...}; typedef struct <tag> *<name>"
|
|
// 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)
|
|
}
|
|
|
|
if t.Align = t.Size; t.Align >= c.ptrSize {
|
|
t.Align = c.ptrSize
|
|
}
|
|
|
|
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,
|
|
}
|
|
// t.Align is the alignment of the Go type.
|
|
t.Align = 1
|
|
}
|
|
|
|
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 <sys/resource.h> 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.
|
|
origOff := off
|
|
off = (off + talign - 1) &^ (talign - 1)
|
|
|
|
if f.ByteOffset > off {
|
|
fld, sizes = c.pad(fld, sizes, f.ByteOffset-origOff)
|
|
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 *<name>".
|
|
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 <name>__{int unused;}; typedef struct <name>__ *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 *<name>".
|
|
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",
|
|
}
|