]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/cgo: rewrite CFTypeRef and subytes on Darwin to uintptr
authorKeith Randall <khr@golang.org>
Fri, 12 Jan 2018 18:08:38 +0000 (10:08 -0800)
committerKeith Randall <khr@golang.org>
Wed, 17 Jan 2018 06:38:57 +0000 (06:38 +0000)
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>
src/cmd/cgo/doc.go
src/cmd/cgo/gcc.go
src/cmd/fix/cftype.go

index 0b64b31d463a4a2f0fd74255bdf846de91e429fc..8e4cd88b37c2f7abe219fd8383bab44183ecba95 100644 (file)
@@ -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
index bf5e3a927b7996ac9a266a4b98c9b4f10f4f99af..4f16fe0e319916e745ed055084d1b5e70a65a987 100644 (file)
@@ -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
 }
 
@@ -546,6 +560,11 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
        // Record types and typedef information.
        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
index 1f06cd6c3397495626f5c8c2f6558215539fb61c..841bd4dccb6285901c19728d98d808d903c5142e 100644 (file)
@@ -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")
        })
 }