Commit d162a297 authored by Keith Randall's avatar Keith Randall

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: default avatarIan Lance Taylor <iant@golang.org>
parent 165e7523
......@@ -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
......
......@@ -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
......
......@@ -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")
})
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment