// // FLEXObjcInternal.mm // FLEX // // Created by Tanner Bennett on 11/1/18. // /* * Copyright (c) 2005-2007 Apple Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ #import "FLEXObjcInternal.h" #import #import #define ALWAYS_INLINE inline __attribute__((always_inline)) #define NEVER_INLINE inline __attribute__((noinline)) // The macros below are copied straight from // objc-internal.h, objc-private.h, objc-object.h, and objc-config.h with // as few modifications as possible. Changes are noted in boxed comments. // https://opensource.apple.com/source/objc4/objc4-723/ // https://opensource.apple.com/source/objc4/objc4-723/runtime/objc-internal.h.auto.html // https://opensource.apple.com/source/objc4/objc4-723/runtime/objc-private.h.auto.html // https://opensource.apple.com/source/objc4/objc4-723/runtime/objc-config.h.auto.html // https://opensource.apple.com/source/objc4/objc4-723/runtime/objc-object.h.auto.html ///////////////////// // objc-internal.h // ///////////////////// #if TARGET_OS_OSX && __x86_64__ // 64-bit Mac - tag bit is LSB # define OBJC_MSB_TAGGED_POINTERS 0 #else // Everything else - tag bit is MSB # define OBJC_MSB_TAGGED_POINTERS 1 #endif #define _OBJC_TAG_INDEX_MASK 0x7 // array slot includes the tag bit itself #define _OBJC_TAG_SLOT_COUNT 16 #define _OBJC_TAG_SLOT_MASK 0xf #define _OBJC_TAG_EXT_INDEX_MASK 0xff // array slot has no extra bits #define _OBJC_TAG_EXT_SLOT_COUNT 256 #define _OBJC_TAG_EXT_SLOT_MASK 0xff #if OBJC_MSB_TAGGED_POINTERS # define _OBJC_TAG_MASK (1UL<<63) # define _OBJC_TAG_INDEX_SHIFT 60 # define _OBJC_TAG_SLOT_SHIFT 60 # define _OBJC_TAG_PAYLOAD_LSHIFT 4 # define _OBJC_TAG_PAYLOAD_RSHIFT 4 # define _OBJC_TAG_EXT_MASK (0xfUL<<60) # define _OBJC_TAG_EXT_INDEX_SHIFT 52 # define _OBJC_TAG_EXT_SLOT_SHIFT 52 # define _OBJC_TAG_EXT_PAYLOAD_LSHIFT 12 # define _OBJC_TAG_EXT_PAYLOAD_RSHIFT 12 #else # define _OBJC_TAG_MASK 1UL # define _OBJC_TAG_INDEX_SHIFT 1 # define _OBJC_TAG_SLOT_SHIFT 0 # define _OBJC_TAG_PAYLOAD_LSHIFT 0 # define _OBJC_TAG_PAYLOAD_RSHIFT 4 # define _OBJC_TAG_EXT_MASK 0xfUL # define _OBJC_TAG_EXT_INDEX_SHIFT 4 # define _OBJC_TAG_EXT_SLOT_SHIFT 4 # define _OBJC_TAG_EXT_PAYLOAD_LSHIFT 0 # define _OBJC_TAG_EXT_PAYLOAD_RSHIFT 12 #endif ////////////////////////////////////// // originally _objc_isTaggedPointer // ////////////////////////////////////// static BOOL flex_isTaggedPointer(const void *ptr) { return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK; } /////////////////// // objc-config.h // /////////////////// // Define SUPPORT_INDEXED_ISA=1 on platforms that store the class in the isa // field as an index into a class table. #if __ARM_ARCH_7K__ >= 2 # define SUPPORT_INDEXED_ISA 1 #else # define SUPPORT_INDEXED_ISA 0 #endif // Define SUPPORT_PACKED_ISA=1 on platforms that store the class in the isa // field as a maskable pointer with other data around it. #if (!__LP64__ || TARGET_OS_WIN32 || TARGET_OS_SIMULATOR) # define SUPPORT_PACKED_ISA 0 #else # define SUPPORT_PACKED_ISA 1 #endif // Define SUPPORT_NONPOINTER_ISA=1 on any platform that may store something // in the isa field that is not a raw pointer. #if !SUPPORT_INDEXED_ISA && !SUPPORT_PACKED_ISA # define SUPPORT_NONPOINTER_ISA 0 #else # define SUPPORT_NONPOINTER_ISA 1 #endif //////////////////// // objc-private.h // //////////////////// union isa_t { isa_t() { } isa_t(uintptr_t value) : bits(value) { } Class cls; uintptr_t bits; #if SUPPORT_PACKED_ISA // extra_rc must be the MSB-most field (so it matches carry/overflow flags) // nonpointer must be the LSB (fixme or get rid of it) // shiftcls must occupy the same bits that a real class pointer would // bits + RC_ONE is equivalent to extra_rc + 1 // RC_HALF is the high bit of extra_rc (i.e. half of its range) // future expansion: // uintptr_t fast_rr : 1; // no r/r overrides // uintptr_t lock : 2; // lock for atomic property, @synch // uintptr_t extraBytes : 1; // allocated with extra bytes # if __arm64__ # define ISA_MASK 0x0000000ffffffff8ULL # define ISA_MAGIC_MASK 0x000003f000000001ULL # define ISA_MAGIC_VALUE 0x000001a000000001ULL struct { uintptr_t nonpointer : 1; uintptr_t has_assoc : 1; uintptr_t has_cxx_dtor : 1; uintptr_t shiftcls : 33; // MACH_VM_MAX_ADDRESS 0x1000000000 uintptr_t magic : 6; uintptr_t weakly_referenced : 1; uintptr_t deallocating : 1; uintptr_t has_sidetable_rc : 1; uintptr_t extra_rc : 19; # define RC_ONE (1ULL<<45) # define RC_HALF (1ULL<<18) }; # elif __x86_64__ # define ISA_MASK 0x00007ffffffffff8ULL # define ISA_MAGIC_MASK 0x001f800000000001ULL # define ISA_MAGIC_VALUE 0x001d800000000001ULL struct { uintptr_t nonpointer : 1; uintptr_t has_assoc : 1; uintptr_t has_cxx_dtor : 1; uintptr_t shiftcls : 44; // MACH_VM_MAX_ADDRESS 0x7fffffe00000 uintptr_t magic : 6; uintptr_t weakly_referenced : 1; uintptr_t deallocating : 1; uintptr_t has_sidetable_rc : 1; uintptr_t extra_rc : 8; # define RC_ONE (1ULL<<56) # define RC_HALF (1ULL<<7) }; # else # error unknown architecture for packed isa # endif // SUPPORT_PACKED_ISA #endif #if SUPPORT_INDEXED_ISA # if __ARM_ARCH_7K__ >= 2 # define ISA_INDEX_IS_NPI 1 # define ISA_INDEX_MASK 0x0001FFFC # define ISA_INDEX_SHIFT 2 # define ISA_INDEX_BITS 15 # define ISA_INDEX_COUNT (1 << ISA_INDEX_BITS) # define ISA_INDEX_MAGIC_MASK 0x001E0001 # define ISA_INDEX_MAGIC_VALUE 0x001C0001 struct { uintptr_t nonpointer : 1; uintptr_t has_assoc : 1; uintptr_t indexcls : 15; uintptr_t magic : 4; uintptr_t has_cxx_dtor : 1; uintptr_t weakly_referenced : 1; uintptr_t deallocating : 1; uintptr_t has_sidetable_rc : 1; uintptr_t extra_rc : 7; # define RC_ONE (1ULL<<25) # define RC_HALF (1ULL<<6) }; # else # error unknown architecture for indexed isa # endif // SUPPORT_INDEXED_ISA #endif }; /////////////////// // objc-object.h // /////////////////// //////////////////////////////////////////////// // originally objc_object::isExtTaggedPointer // //////////////////////////////////////////////// static BOOL flex_isExtTaggedPointer(const void *ptr) { return ((uintptr_t)ptr & _OBJC_TAG_EXT_MASK) == _OBJC_TAG_EXT_MASK; } struct flex_objc_object { isa_t isa; }; ////////////////////////////////////////////////////////// // Returns nil on platforms without nonpointer isa. // // Supporting those platforms would be too complicated // // for such a niche feature anyway. - @NSExceptional // // // // Code modified from objc_object::ISA() on 11/04/18 // ////////////////////////////////////////////////////////// static id flex_getIsa(const flex_objc_object *object) { #if SUPPORT_NONPOINTER_ISA if (object->isa.nonpointer) { return object_getClass((__bridge id)object); } return (__bridge Class)(void *)object->isa.bits; #else return nil; #endif } ///////////////////////////////////// // FLEXObjectInternal // // No Apple code beyond this point // ///////////////////////////////////// extern "C" { /// Assumes memory is valid and readable. /// https://blog.timac.org/2016/1124-testing-if-an-arbitrary-pointer-is-a-valid-objective-c-object/ BOOL FLEXPointerIsValidObjcObject(const void * ptr) { uintptr_t pointer = (uintptr_t)ptr; if (!ptr) { return NO; } // Tagged pointers have 0x1 set, no other valid pointers do // objc-internal.h -> _objc_isTaggedPointer() if (flex_isTaggedPointer(ptr) || flex_isExtTaggedPointer(ptr)) { return YES; } // Check pointer alignment if ((pointer % sizeof(uintptr_t)) != 0) { return NO; } // From LLDB: // Pointers in a class_t will only have bits 0 through 46 set, // so if any pointer has bits 47 through 63 high, we know that this is not a valid isa // http://llvm.org/svn/llvm-project/lldb/trunk/examples/summaries/cocoa/objc_runtime.py if ((pointer & 0xFFFF800000000000) != 0) { return NO; } // http://www.sealiesoftware.com/blog/archive/2013/09/24/objc_explain_Non-pointer_isa.html : if (flex_getIsa((const flex_objc_object *)ptr)) { return YES; } return NO; } }