blob: 81f235b161de87fa490474a08353915684e0ab58 [file] [log] [blame]
#include "OldABI.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <unistd.h>
#include <sys/mman.h>
#include <malloc/malloc.h>
#include <objc/runtime.h>
#include <Foundation/Foundation.h>
// Implementation of ObjC classes
// with bits set to mimic the pre-stable Swift ABI
// and additional memory protection to detect mis-use
#if __has_include(<objc/objc-internal.h>)
#include <objc/objc-internal.h>
#else
extern "C" Class objc_initializeClassPair(Class superclass, const char *name, Class cls, Class metacls);
extern "C" id _objc_rootRetain(id);
extern "C" void _objc_rootRelease(id);
extern "C" id _objc_rootAutorelease(id);
extern "C" uintptr_t _objc_rootRetainCount(id);
extern "C" bool _objc_rootTryRetain(id);
extern "C" bool _objc_rootIsDeallocating(id);
#endif
// This class stands in for the pre-stable ABI's SwiftObject.
// Stable Swift's class is named Swift._SwiftObject (but mangled).
__attribute__((objc_root_class))
@interface SwiftObject { id isa; } @end
@implementation SwiftObject
+(void)initialize { }
+(id)allocWithZone:(struct _malloc_zone_t *)zone {
return class_createInstance(self, 0);
}
+(id)alloc { return [self allocWithZone:nil]; }
+(id)class { return self; }
-(id)class { return object_getClass(self); }
+(id)superclass { return class_getSuperclass(self); }
-(id)superclass { return class_getSuperclass([self class]); }
+(BOOL)isMemberOfClass:(Class)cls { return object_getClass(self) == cls; }
-(BOOL)isMemberOfClass:(Class)cls { return [self class] == cls; }
-(id)self { return self; }
-(BOOL)isProxy { return NO; }
-(struct _malloc_zone_t *)zone { return malloc_default_zone(); }
-(void)doesNotRecognizeSelector:(SEL)sel {
Class cls = [self class];
fprintf(stderr, "unrecognized selector %c[%s %s]\n",
class_isMetaClass(cls) ? '+' : '-',
class_getName(cls), sel_getName(sel));
abort();
}
+(id)retain { return self; }
+(void)release { }
+(id)autorelease { return self; }
+(uintptr_t)retainCount { return ~(uintptr_t)0; }
+(BOOL)_tryRetain { return YES; }
+(BOOL)_isDeallocating { return NO; }
-(id)retain { return _objc_rootRetain(self); }
-(void)release { _objc_rootRelease(self); }
-(id)autorelease { return _objc_rootAutorelease(self); }
-(uintptr_t)retainCount { return _objc_rootRetainCount(self); }
-(BOOL)_tryRetain { return _objc_rootTryRetain(self); }
-(BOOL)_isDeallocating { return _objc_rootIsDeallocating(self); }
-(void)dealloc { object_dispose(self); }
-(BOOL)isKindOfClass:(Class)other {
for (Class cls = object_getClass(self); cls; cls = class_getSuperclass(cls))
if (cls == other) return YES;
return NO;
}
+(BOOL)isSubclassOfClass:(Class)other {
for (Class cls = self; cls; cls = class_getSuperclass(cls))
if (cls == other) return YES;
return NO;
}
-(BOOL)respondsToSelector:(SEL)sel {
if (!sel) return NO;
return class_respondsToSelector(object_getClass(self), sel);
}
+(BOOL)instancesRespondToSelector:(SEL)sel {
if (!sel) return NO;
return class_respondsToSelector(self, sel);
}
-(uintptr_t)hash { return (uintptr_t)self; }
-(BOOL)isEqual:(id)other { return self == other; }
+(NSString *)description { return @"FakeSwiftObject class"; }
-(NSString *)description { return @"FakeSwiftObject instance"; }
-(NSString *)debugDescription { return [self description]; }
- (BOOL)isNSArray__ { return NO; }
- (BOOL)isNSCFConstantString__ { return NO; }
- (BOOL)isNSData__ { return NO; }
- (BOOL)isNSDate__ { return NO; }
- (BOOL)isNSDictionary__ { return NO; }
- (BOOL)isNSObject__ { return NO; }
- (BOOL)isNSOrderedSet__ { return NO; }
- (BOOL)isNSNumber__ { return NO; }
- (BOOL)isNSSet__ { return NO; }
- (BOOL)isNSString__ { return NO; }
- (BOOL)isNSTimeZone__ { return NO; }
- (BOOL)isNSValue__ { return NO; }
@end
static char *AllocTailGuardedPointer(size_t size) {
// Round up to page boundary.
size_t writeableSize = (size + PAGE_MAX_SIZE - 1) & ~(PAGE_MAX_SIZE - 1);
// Allocate writeable memory plus one guard page.
char *writeableBuffer = (char *)mmap(0, writeableSize + PAGE_MAX_SIZE,
PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE, -1, 0);
if (writeableBuffer == MAP_FAILED) abort();
// Mark the guard page inaccessible.
mprotect(writeableBuffer + writeableSize, PAGE_MAX_SIZE, 0);
// Scribble on the prefix.
memset(writeableBuffer, 0x55, writeableSize - size);
// Return the address just before the guard page.
// FIXME: this doesn't handle alignment properly,
// but we don't need to for this test's usage.
return writeableBuffer + writeableSize - size;
}
static Class CreateOldABISubclass(Class supercls, const char *name) {
// Allocate class and metaclass in tail-guarded memory.
// If the Swift runtime incorrectly tries to read Swift
// metadata from this class then it'll crash.
char *clsbuf = AllocTailGuardedPointer(5*sizeof(uintptr_t));
char *metabuf = AllocTailGuardedPointer(5*sizeof(uintptr_t));
Class result = objc_initializeClassPair(supercls, name,
(Class)clsbuf, (Class)metabuf);
// Set the old is-Swift bit in the class.
uintptr_t *words = (uintptr_t *)clsbuf;
words[4] |= 1;
return result;
}
static Class FakeOldABIClass;
__attribute__((constructor))
static void initialize(void) {
FakeOldABIClass = CreateOldABISubclass([SwiftObject class],
"_TtC6OldABI8Subclass");
}
bool CanTestOldABI() {
// These tests don't work until the stable ABI is using its designed bit.
// This check can be removed after SWIFT_DARWIN_ENABLE_STABLE_ABI_BIT
// is set everywhere.
Class cls = objc_getClass("_TtCs19__EmptyArrayStorage");
if (!cls) abort();
uintptr_t *words = (uintptr_t *)cls;
if ((words[4] & 3) != 2) return false; // wrong stable is-Swift bit
return true;
}
id AllocOldABIObject() {
return [FakeOldABIClass alloc];
}