aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKeith Randall <khr@golang.org>2018-01-12 10:08:38 -0800
committerKeith Randall <khr@golang.org>2018-01-17 06:38:57 +0000
commitd162a297ed216fb02ebe409ace2387c3a656de66 (patch)
tree22ebd7ad45ee9eb149fcc36c6a686f5cc05ab40f
parent165e7523fb627cc7f6be56b7318fea34d73d7167 (diff)
downloadgo-d162a297ed216fb02ebe409ace2387c3a656de66.tar.gz
go-d162a297ed216fb02ebe409ace2387c3a656de66.zip
cmd/cgo: rewrite CFTypeRef and subytes on Darwin to uintptr
Cgo currently maps CFTypeRef and its subtypes to unsafe.Pointer or a pointer to a named empty struct. However, Darwin sometimes encodes some of CFTypeRef's subtypes as a few int fields packed in a pointer wrapper. This hackery confuses the Go runtime as the pointers can look like they point to things that shouldn't be pointed at. Switch CFTypeRef and its subtypes to map to uintptr. Detecting the affected set of types is tricky, there are over 200 of them, and the set isn't static across Darwin versions. Fortunately, downcasting from CFTypeRef to a subtype requires calling CFGetTypeID, getting a CFTypeID token, and comparing that with a known id from a *GetTypeID() call. So we can find all the type names by detecting all the *GetTypeID() prototypes and rewriting the corresponding *Ref types to uintptr. This strategy covers all the cases I've checked and is unlikely to have a false positive. Update #23091. Change-Id: I487eb4105c9b4785ba564de9c38d472c8c9a76ac Reviewed-on: https://go-review.googlesource.com/87615 Run-TryBot: Keith Randall <khr@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
-rw-r--r--src/cmd/cgo/doc.go68
-rw-r--r--src/cmd/cgo/gcc.go63
-rw-r--r--src/cmd/fix/cftype.go6
3 files changed, 61 insertions, 76 deletions
diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go
index 0b64b31d46..8e4cd88b37 100644
--- a/src/cmd/cgo/doc.go
+++ b/src/cmd/cgo/doc.go
@@ -341,69 +341,11 @@ in unexpected and unpredictable ways.
Special cases
A few special C types which would normally be represented by a pointer
-type in Go are instead represented by a uintptr. Those types are
-the CF*Ref types from the CoreFoundation library on Darwin, including:
-
- CFAllocatorRef
- CFArrayRef
- CFAttributedStringRef
- CFBagRef
- CFBinaryHeapRef
- CFBitVectorRef
- CFBooleanRef
- CFBundleRef
- CFCalendarRef
- CFCharacterSetRef
- CFDataRef
- CFDateFormatterRef
- CFDateRef
- CFDictionaryRef
- CFErrorRef
- CFFileDescriptorRef
- CFFileSecurityRef
- CFLocaleRef
- CFMachPortRef
- CFMessagePortRef
- CFMutableArrayRef
- CFMutableAttributedStringRef
- CFMutableBagRef
- CFMutableBitVectorRef
- CFMutableCharacterSetRef
- CFMutableDataRef
- CFMutableDictionaryRef
- CFMutableSetRef
- CFMutableStringRef
- CFNotificationCenterRef
- CFNullRef
- CFNumberFormatterRef
- CFNumberRef
- CFPlugInInstanceRef
- CFPlugInRef
- CFPropertyListRef
- CFReadStreamRef
- CFRunLoopObserverRef
- CFRunLoopRef
- CFRunLoopSourceRef
- CFRunLoopTimerRef
- CFSetRef
- CFSocketRef
- CFStringRef
- CFStringTokenizerRef
- CFTimeZoneRef
- CFTreeRef
- CFTypeRef
- CFURLCreateFromFSRef
- CFURLEnumeratorRef
- CFURLGetFSRef
- CFURLRef
- CFUUIDRef
- CFUserNotificationRef
- CFWriteStreamRef
- CFXMLNodeRef
- CFXMLParserRef
- CFXMLTreeRef
-
-Also the object types from Java's JNI interface:
+type in Go are instead represented by a uintptr. Those include:
+
+1. The *Ref types on Darwin, rooted at CoreFoundation's CFTypeRef type.
+
+2. The object types from Java's JNI interface:
jobject
jclass
diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go
index bf5e3a927b..4f16fe0e31 100644
--- a/src/cmd/cgo/gcc.go
+++ b/src/cmd/cgo/gcc.go
@@ -224,6 +224,7 @@ 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
@@ -260,6 +261,14 @@ func (p *Package) guessKinds(f *File) []*Name {
continue
}
+ if goos == "darwin" && 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)
}
@@ -406,6 +415,11 @@ func (p *Package) guessKinds(f *File) []*Name {
for i, n := range names {
switch sniff[i] {
default:
+ if sniff[i]&notDeclared != 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"
@@ -418,6 +432,7 @@ func (p *Package) guessKinds(f *File) []*Name {
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,
@@ -431,7 +446,6 @@ func (p *Package) guessKinds(f *File) []*Name {
fatalf("unresolved names")
}
- needType = append(needType, names...)
return needType
}
@@ -547,6 +561,11 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
var conv typeConv
conv.Init(p.PtrSize, p.IntSize)
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
}
@@ -1642,6 +1661,9 @@ type typeConv struct {
// Keys of ptrs in insertion order (deterministic worklist)
ptrKeys []dwarf.Type
+ // Type names X for which there exists an XGetTypeID function with type func() CFTypeID.
+ getTypeIDs map[string]bool
+
// Predeclared types.
bool ast.Expr
byte ast.Expr // denotes padding
@@ -1671,6 +1693,7 @@ func (c *typeConv) Init(ptrSize, intSize int64) {
c.intSize = intSize
c.m = make(map[dwarf.Type]*Type)
c.ptrs = make(map[dwarf.Type][]*Type)
+ c.getTypeIDs = make(map[string]bool)
c.bool = c.Ident("bool")
c.byte = c.Ident("byte")
c.int8 = c.Ident("int8")
@@ -2057,7 +2080,7 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
name := c.Ident("_Ctype_" + dt.Name)
goIdent[name.Name] = name
sub := c.Type(dt.Type, pos)
- if badPointerTypedef(dt) {
+ if c.badPointerTypedef(dt) {
// Treat this typedef as a uintptr.
s := *sub
s.Go = c.uintptr
@@ -2223,7 +2246,7 @@ func (c *typeConv) FuncArg(dtype dwarf.Type, pos token.Pos) *Type {
}
// ...or the typedef is one in which we expect bad pointers.
// It will be a uintptr instead of *X.
- if badPointerTypedef(dt) {
+ if c.badPointerTypedef(dt) {
break
}
@@ -2571,23 +2594,43 @@ func fieldPrefix(fld []*ast.Field) string {
// 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.
-func badPointerTypedef(dt *dwarf.TypedefType) bool {
- if badCFType(dt) {
+func (c *typeConv) badPointerTypedef(dt *dwarf.TypedefType) bool {
+ if c.badCFType(dt) {
return true
}
- if badJNI(dt) {
+ if c.badJNI(dt) {
return true
}
return false
}
-func badCFType(dt *dwarf.TypedefType) bool {
+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 CF*Ref types just so casting between them is easier.
+ // 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.
- return goos == "darwin" && strings.HasPrefix(dt.Name, "CF") && strings.HasSuffix(dt.Name, "Ref")
+ if goos != "darwin" {
+ 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
@@ -2625,7 +2668,7 @@ enum {
};
*/
-func badJNI(dt *dwarf.TypedefType) bool {
+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
diff --git a/src/cmd/fix/cftype.go b/src/cmd/fix/cftype.go
index 1f06cd6c33..841bd4dccb 100644
--- a/src/cmd/fix/cftype.go
+++ b/src/cmd/fix/cftype.go
@@ -19,7 +19,7 @@ var cftypeFix = fix{
name: "cftype",
date: "2017-09-27",
f: cftypefix,
- desc: `Fixes initializers of C.CF*Ptr types`,
+ desc: `Fixes initializers of C.*Ref types`,
disabled: false,
}
@@ -27,11 +27,11 @@ var cftypeFix = fix{
// type CFTypeRef unsafe.Pointer
// New state:
// type CFTypeRef uintptr
-// and similar for other CF*Ref types.
+// and similar for other *Ref types.
// This fix finds nils initializing these types and replaces the nils with 0s.
func cftypefix(f *ast.File) bool {
return typefix(f, func(s string) bool {
- return strings.HasPrefix(s, "C.CF") && strings.HasSuffix(s, "Ref")
+ return strings.HasPrefix(s, "C.") && strings.HasSuffix(s, "Ref")
})
}