diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/KSCrash-Package.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/KSCrash-Package.xcscheme index 4ca2f233a..42930a35b 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/KSCrash-Package.xcscheme +++ b/.swiftpm/xcode/xcshareddata/xcschemes/KSCrash-Package.xcscheme @@ -135,7 +135,10 @@ Identifier = "KSDebug_Tests"> + Identifier = "KSObjC_Tests/testArrayDescription"> + + diff --git a/KSCrash.podspec b/KSCrash.podspec index d2a332a07..283b97840 100644 --- a/KSCrash.podspec +++ b/KSCrash.podspec @@ -1,5 +1,5 @@ Pod::Spec.new do |s| - IOS_DEPLOYMENT_TARGET = '11.0' unless defined? IOS_DEPLOYMENT_TARGET + IOS_DEPLOYMENT_TARGET = '12.0' unless defined? IOS_DEPLOYMENT_TARGET s.name = "KSCrash" s.version = "1.17.1" s.summary = "The Ultimate iOS Crash Reporter" @@ -7,9 +7,9 @@ Pod::Spec.new do |s| s.license = { :type => 'KSCrash license agreement', :file => 'LICENSE' } s.author = { "Karl Stenerud" => "kstenerud@gmail.com" } s.ios.deployment_target = IOS_DEPLOYMENT_TARGET - s.osx.deployment_target = '10.13' - s.tvos.deployment_target = '11.0' - s.watchos.deployment_target = '4.0' + s.osx.deployment_target = '10.14' + s.tvos.deployment_target = '12.0' + s.watchos.deployment_target = '5.0' s.visionos.deployment_target = '1.0' s.source = { :git => "https://github.com/kstenerud/KSCrash.git", :tag=>s.version.to_s } s.frameworks = 'Foundation' diff --git a/Package.swift b/Package.swift index f9030099d..75610d128 100644 --- a/Package.swift +++ b/Package.swift @@ -5,10 +5,10 @@ import PackageDescription let package = Package( name: "KSCrash", platforms: [ - .iOS(.v11), - .tvOS(.v11), - .watchOS(.v4), - .macOS(.v10_13), + .iOS(.v12), + .tvOS(.v12), + .watchOS(.v5), + .macOS(.v10_14), ], products: [ .library( diff --git a/Sources/KSCrashRecordingCore/KSObjC.c b/Sources/KSCrashRecordingCore/KSObjC.c index cf3cf7025..349620c64 100644 --- a/Sources/KSCrashRecordingCore/KSObjC.c +++ b/Sources/KSCrashRecordingCore/KSObjC.c @@ -156,14 +156,16 @@ static const char* g_blockBaseClassName = "NSBlock"; #pragma mark - Utility - //====================================================================== -#if SUPPORT_TAGGED_POINTERS -static bool isTaggedPointer(const void* pointer) {return (((uintptr_t)pointer) & TAG_MASK) != 0; } -static int getTaggedSlot(const void* pointer) { return (int)((((uintptr_t)pointer) >> TAG_SLOT_SHIFT) & TAG_SLOT_MASK); } -static uintptr_t getTaggedPayload(const void* pointer) { return (((uintptr_t)pointer) << TAG_PAYLOAD_LSHIFT) >> TAG_PAYLOAD_RSHIFT; } +#if OBJC_HAVE_TAGGED_POINTERS +static bool isTaggedPointer(const void* pointer) { return _objc_isTaggedPointer(pointer); } +static int getTaggedSlot(const void* pointer) { return (int)_objc_getTaggedPointerTag(pointer); } +static uintptr_t getTaggedPayload(const void* pointer) { return _objc_getTaggedPointerValue(pointer); } +static intptr_t getTaggedSignedPayload(const void* pointer) { return _objc_getTaggedPointerSignedValue(pointer); } #else static bool isTaggedPointer(__unused const void* pointer) { return false; } static int getTaggedSlot(__unused const void* pointer) { return 0; } static uintptr_t getTaggedPayload(const void* pointer) { return (uintptr_t)pointer; } +static intptr_t getTaggedSignedPayload(const void* pointer) { return (intptr_t)pointer; } #endif /** Get class data for a tagged pointer. @@ -306,13 +308,8 @@ static bool isTaggedPointerNSDate(const void* const object) */ static int64_t extractTaggedNSNumber(const void* const object) { - intptr_t signedPointer = (intptr_t)object; -#if SUPPORT_TAGGED_POINTERS - intptr_t value = (signedPointer << TAG_PAYLOAD_LSHIFT) >> TAG_PAYLOAD_RSHIFT; -#else - intptr_t value = signedPointer & 0; -#endif - + intptr_t value = getTaggedSignedPayload(object); + // The lower 4 bits encode type information so shift them out. return (int64_t)(value >> 4); } @@ -363,22 +360,83 @@ static int extractTaggedNSString(const void* const object, char* buffer, int buf return length; } +#if OBJC_HAVE_TAGGED_POINTERS +/** Decodes the exponent of a tagged NSDate pointer. + * + * @param exp The 7-bit exponent value from the tagged NSDate pointer. + * @return The decoded exponent value as a 64-bit unsigned integer. + * + * @note This function is based on the LLVM code in the Cocoa.cpp file: https://github.com/apple/llvm-project/blob/5dc9d563e5a6cd2cdd44117697dead98955ccddf/lldb/source/Plugins/Language/ObjC/Cocoa.cpp#L934 + */ +static uint64_t decodeExponent(uint64_t exp) +{ + // Bias value for tagged pointer exponents. + // Recommended values: + // 0x3e3: encodes all dates between distantPast and distantFuture + // except for the range within about 1e-28 second of the reference date. + // 0x3ef: encodes all dates for a few million years beyond distantPast and + // distantFuture, except within about 1e-25 second of the reference date. + static const int taggedDateExponentBias = 0x3ef; + + // Sign-extend the 7-bit exponent to 64 bits + const uint64_t signBit = 1ULL << 6; + const uint64_t extendMask = ~((1ULL << 7) - 1); + exp = (exp ^ signBit) - signBit; + exp &= (1ULL << 7) - 1; + exp |= extendMask & -((exp & signBit) != 0); + + // Add the bias to the sign-extended exponent + return exp + taggedDateExponentBias; +} /** Extract a tagged NSDate's time value as an absolute time. * * @param object The NSDate object (must be a tagged pointer). * @return The date's absolute time. + * + * @note This function is based on the LLVM code in the Cocoa.cpp file: https://github.com/apple/llvm-project/blob/5dc9d563e5a6cd2cdd44117697dead98955ccddf/lldb/source/Plugins/Language/ObjC/Cocoa.cpp#L913-L958 */ static CFAbsoluteTime extractTaggedNSDate(const void* const object) { - uintptr_t payload = getTaggedPayload(object); - // Payload is a 60-bit float. Fortunately we can just cast across from - // an integer pointer after shifting out the upper 4 bits. - payload <<= 4; - CFAbsoluteTime value = *((CFAbsoluteTime*)&payload); - return value; -} + #pragma pack(4) + union { + uintptr_t raw; + struct { + uint64_t fraction : 52; + uint64_t exponent : 7; + uint64_t sign : 1; + uint64_t unused : 4; + } bits; + } encodedBits = { .raw = getTaggedPayload(object) }; + + if (encodedBits.raw == 0) + { + return 0.0; + } + if (encodedBits.raw == UINT64_MAX) + { + return -0.0; + } + + #pragma pack(4) + union { + CFAbsoluteTime value; + struct { + uint64_t fraction : 52; + uint64_t exponent : 11; + uint64_t sign : 1; + } bits; + } decodedBits = { + .bits = { + .fraction = encodedBits.bits.fraction, + .exponent = decodeExponent(encodedBits.bits.exponent), + .sign = encodedBits.bits.sign + } + }; + return decodedBits.value; +} +#endif /** Get any special class metadata we have about the specified class. * It will return a generic metadata object if the type is not recognized. * @@ -856,6 +914,7 @@ bool ksobjc_ivarNamed(const void* const classPtr, const char* name, KSObjCIvar* bool ksobjc_ivarValue(const void* const objectPtr, int ivarIndex, void* dst) { +#if OBJC_HAVE_TAGGED_POINTERS if(isTaggedPointer(objectPtr)) { // Naively assume they want "value". @@ -874,7 +933,7 @@ bool ksobjc_ivarValue(const void* const objectPtr, int ivarIndex, void* dst) } return false; } - +#endif const void* const classPtr = getIsaPointer(objectPtr); const struct ivar_list_t* ivars = getClassRO(classPtr)->ivars; if(ivarIndex >= (int)ivars->count) @@ -1291,10 +1350,12 @@ static bool dateIsValid(const void* const datePtr) CFAbsoluteTime ksobjc_dateContents(const void* const datePtr) { +#if OBJC_HAVE_TAGGED_POINTERS if(isValidTaggedPointer(datePtr)) { return extractTaggedNSDate(datePtr); } +#endif const struct __CFDate* date = datePtr; return date->_time; } @@ -1318,6 +1379,7 @@ static bool taggedDateIsValid(const void* const datePtr) static int taggedDateDescription(const void* object, char* buffer, int bufferLength) { +#if OBJC_HAVE_TAGGED_POINTERS char* pBuffer = buffer; char* pEnd = buffer + bufferLength; @@ -1326,6 +1388,9 @@ static int taggedDateDescription(const void* object, char* buffer, int bufferLen pBuffer += stringPrintf(pBuffer, (int)(pEnd - pBuffer), ": %f", time); return (int)(pBuffer - buffer); +#else + return 0; +#endif } diff --git a/Sources/KSCrashRecordingCore/include/KSObjCApple.h b/Sources/KSCrashRecordingCore/include/KSObjCApple.h index 5a92c2328..a4ce0a6aa 100644 --- a/Sources/KSCrashRecordingCore/include/KSObjCApple.h +++ b/Sources/KSCrashRecordingCore/include/KSObjCApple.h @@ -57,71 +57,40 @@ NAME { \ # define ISA_MASK ~1UL #endif - // ====================================================================== -#pragma mark - objc4-680/runtime/objc-config.h - +#pragma mark - objc4-912.3/runtime/objc-config.h - // ====================================================================== -// Define SUPPORT_TAGGED_POINTERS=1 to enable tagged pointer objects -// Be sure to edit tagged pointer SPI in objc-internal.h as well. -#if !(__LP64__) -# define SUPPORT_TAGGED_POINTERS 0 -#else -# define SUPPORT_TAGGED_POINTERS 1 -#endif - -// Define SUPPORT_MSB_TAGGED_POINTERS to use the MSB -// as the tagged pointer marker instead of the LSB. -// Be sure to edit tagged pointer SPI in objc-internal.h as well. -#if !SUPPORT_TAGGED_POINTERS || !TARGET_OS_IPHONE -# define SUPPORT_MSB_TAGGED_POINTERS 0 +#if __ARM_ARCH_7K__ >= 2 || (__arm64__ && !__LP64__) +# define SUPPORT_INDEXED_ISA 1 #else -# define SUPPORT_MSB_TAGGED_POINTERS 1 +# define SUPPORT_INDEXED_ISA 0 #endif - // ====================================================================== -#pragma mark - objc4-680/runtime/objc-object.h - +#pragma mark - objc4-912.3/runtime/objc-internal.h - +// With removed nullability to suppress warning // ====================================================================== - -#if SUPPORT_TAGGED_POINTERS - -// KS: The original values wouldn't have worked. The slot shift and mask -// were incorrect. -#define TAG_COUNT 8 -//#define TAG_SLOT_MASK 0xf -#define TAG_SLOT_MASK 0x07 - -#if SUPPORT_MSB_TAGGED_POINTERS -# define TAG_MASK (1ULL<<63) -# define TAG_SLOT_SHIFT 60 -# define TAG_PAYLOAD_LSHIFT 4 -# define TAG_PAYLOAD_RSHIFT 4 -#else -# define TAG_MASK 1 -//# define TAG_SLOT_SHIFT 0 -# define TAG_SLOT_SHIFT 1 -# define TAG_PAYLOAD_LSHIFT 0 -# define TAG_PAYLOAD_RSHIFT 4 -#endif +#if __LP64__ +#define OBJC_HAVE_TAGGED_POINTERS 1 #endif -// ====================================================================== -#pragma mark - objc4-781/runtime/objc-internal.h - -// ====================================================================== -#if __ARM_ARCH_7K__ >= 2 || (__arm64__ && !__LP64__) -# define SUPPORT_INDEXED_ISA 1 -#else -# define SUPPORT_INDEXED_ISA 0 -#endif +// Tagged pointer layout and usage is subject to change on different OS versions. -// ====================================================================== -#pragma mark - objc4-680/runtime/objc-internal.h - -// ====================================================================== +// Tag indexes 0..<7 have a 60-bit payload. +// Tag index 7 is reserved. +// Tag indexes 8..<264 have a 52-bit payload. +// Tag index 264 is reserved. +#if __has_feature(objc_fixed_enum) || __cplusplus >= 201103L +enum objc_tag_index_t : uint16_t +#else +typedef uint16_t objc_tag_index_t; enum +#endif { + // 60-bit payloads OBJC_TAG_NSAtom = 0, OBJC_TAG_1 = 1, OBJC_TAG_NSString = 2, @@ -129,10 +98,377 @@ enum OBJC_TAG_NSIndexPath = 4, OBJC_TAG_NSManagedObjectID = 5, OBJC_TAG_NSDate = 6, - OBJC_TAG_7 = 7 + + // 60-bit reserved + OBJC_TAG_RESERVED_7 = 7, + + // 52-bit payloads + OBJC_TAG_Photos_1 = 8, + OBJC_TAG_Photos_2 = 9, + OBJC_TAG_Photos_3 = 10, + OBJC_TAG_Photos_4 = 11, + OBJC_TAG_XPC_1 = 12, + OBJC_TAG_XPC_2 = 13, + OBJC_TAG_XPC_3 = 14, + OBJC_TAG_XPC_4 = 15, + OBJC_TAG_NSColor = 16, + OBJC_TAG_UIColor = 17, + OBJC_TAG_CGColor = 18, + OBJC_TAG_NSIndexSet = 19, + OBJC_TAG_NSMethodSignature = 20, + OBJC_TAG_UTTypeRecord = 21, + OBJC_TAG_Foundation_1 = 22, + OBJC_TAG_Foundation_2 = 23, + OBJC_TAG_Foundation_3 = 24, + OBJC_TAG_Foundation_4 = 25, + OBJC_TAG_CGRegion = 26, + + // When using the split tagged pointer representation + // (OBJC_SPLIT_TAGGED_POINTERS), this is the first tag where + // the tag and payload are unobfuscated. All tags from here to + // OBJC_TAG_Last52BitPayload are unobfuscated. The shared cache + // builder is able to construct these as long as the low bit is + // not set (i.e. even-numbered tags). + OBJC_TAG_FirstUnobfuscatedSplitTag = 136, // 128 + 8, first ext tag with high bit set + + OBJC_TAG_Constant_CFString = 136, + + OBJC_TAG_First60BitPayload = 0, + OBJC_TAG_Last60BitPayload = 6, + OBJC_TAG_First52BitPayload = 8, + OBJC_TAG_Last52BitPayload = 263, + + OBJC_TAG_RESERVED_264 = 264 }; +#if __has_feature(objc_fixed_enum) && !defined(__cplusplus) +typedef enum objc_tag_index_t objc_tag_index_t; +#endif + +#if OBJC_HAVE_TAGGED_POINTERS // (KSCrash) This line is moved here to make `objc_tag_index_t` enum visible for i386. + +// Returns true if tagged pointers are enabled. +// The other functions below must not be called if tagged pointers are disabled. +static inline bool +_objc_taggedPointersEnabled(void); + +// Create a tagged pointer object with the given tag and payload. +// Assumes the tag is valid. +// Assumes tagged pointers are enabled. +// The payload will be silently truncated to fit. +static inline void * +_objc_makeTaggedPointer(objc_tag_index_t tag, uintptr_t payload); + +// Return true if ptr is a tagged pointer object. +// Does not check the validity of ptr's class. +static inline bool +_objc_isTaggedPointer(const void *ptr); + +// Extract the tag value from the given tagged pointer object. +// Assumes ptr is a valid tagged pointer object. +// Does not check the validity of ptr's tag. +static inline objc_tag_index_t +_objc_getTaggedPointerTag(const void *ptr); + +// Extract the payload from the given tagged pointer object. +// Assumes ptr is a valid tagged pointer object. +// The payload value is zero-extended. +static inline uintptr_t +_objc_getTaggedPointerValue(const void *ptr); + +// Extract the payload from the given tagged pointer object. +// Assumes ptr is a valid tagged pointer object. +// The payload value is sign-extended. +static inline intptr_t +_objc_getTaggedPointerSignedValue(const void *ptr); + +// Don't use the values below. Use the declarations above. + +#if __arm64__ +// ARM64 uses a new tagged pointer scheme where normal tags are in +// the low bits, extended tags are in the high bits, and half of the +// extended tag space is reserved for unobfuscated payloads. +# define OBJC_SPLIT_TAGGED_POINTERS 1 +#else +# define OBJC_SPLIT_TAGGED_POINTERS 0 +#endif +#if (TARGET_OS_OSX || TARGET_OS_MACCATALYST) && __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 0x7UL + +#if OBJC_SPLIT_TAGGED_POINTERS +#define _OBJC_TAG_SLOT_COUNT 8 +#define _OBJC_TAG_SLOT_MASK 0x7UL +#else +// array slot includes the tag bit itself +#define _OBJC_TAG_SLOT_COUNT 16 +#define _OBJC_TAG_SLOT_MASK 0xfUL +#endif + +#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_SPLIT_TAGGED_POINTERS +# define _OBJC_TAG_MASK (1UL<<63) +# define _OBJC_TAG_INDEX_SHIFT 0 +# define _OBJC_TAG_SLOT_SHIFT 0 +# define _OBJC_TAG_PAYLOAD_LSHIFT 1 +# define _OBJC_TAG_PAYLOAD_RSHIFT 4 +# define _OBJC_TAG_EXT_MASK (_OBJC_TAG_MASK | 0x7UL) +# define _OBJC_TAG_NO_OBFUSCATION_MASK ((1UL<<62) | _OBJC_TAG_EXT_MASK) +# define _OBJC_TAG_CONSTANT_POINTER_MASK \ +~(_OBJC_TAG_EXT_MASK | ((uintptr_t)_OBJC_TAG_EXT_SLOT_MASK << _OBJC_TAG_EXT_SLOT_SHIFT)) +# define _OBJC_TAG_EXT_INDEX_SHIFT 55 +# define _OBJC_TAG_EXT_SLOT_SHIFT 55 +# define _OBJC_TAG_EXT_PAYLOAD_LSHIFT 9 +# define _OBJC_TAG_EXT_PAYLOAD_RSHIFT 12 +#elif 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 + +// Map of tags to obfuscated tags. +extern uintptr_t objc_debug_taggedpointer_obfuscator; + +#if OBJC_SPLIT_TAGGED_POINTERS +extern uint8_t objc_debug_tag60_permutations[8]; + +static inline uintptr_t _objc_basicTagToObfuscatedTag(uintptr_t tag) { + return objc_debug_tag60_permutations[tag]; +} + +static inline uintptr_t _objc_obfuscatedTagToBasicTag(uintptr_t tag) { + for (unsigned i = 0; i < 7; i++) + if (objc_debug_tag60_permutations[i] == tag) + return i; + return 7; +} +#endif + +static inline void * +_objc_encodeTaggedPointer_withObfuscator(uintptr_t ptr, uintptr_t obfuscator) +{ + uintptr_t value = (obfuscator ^ ptr); +#if OBJC_SPLIT_TAGGED_POINTERS + if ((value & _OBJC_TAG_NO_OBFUSCATION_MASK) == _OBJC_TAG_NO_OBFUSCATION_MASK) + return (void *)ptr; + uintptr_t basicTag = (value >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK; + uintptr_t permutedTag = _objc_basicTagToObfuscatedTag(basicTag); + value &= ~(_OBJC_TAG_INDEX_MASK << _OBJC_TAG_INDEX_SHIFT); + value |= permutedTag << _OBJC_TAG_INDEX_SHIFT; +#endif + return (void *)value; +} + +static inline uintptr_t +_objc_decodeTaggedPointer_noPermute_withObfuscator(const void *ptr, + uintptr_t obfuscator) +{ + uintptr_t value = (uintptr_t)ptr; +#if OBJC_SPLIT_TAGGED_POINTERS + if ((value & _OBJC_TAG_NO_OBFUSCATION_MASK) == _OBJC_TAG_NO_OBFUSCATION_MASK) + return value; +#endif + return value ^ obfuscator; +} +static inline uintptr_t +_objc_decodeTaggedPointer_withObfuscator(const void *ptr, + uintptr_t obfuscator) +{ + uintptr_t value + = _objc_decodeTaggedPointer_noPermute_withObfuscator(ptr, obfuscator); +#if OBJC_SPLIT_TAGGED_POINTERS + uintptr_t basicTag = (value >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK; + + value &= ~(_OBJC_TAG_INDEX_MASK << _OBJC_TAG_INDEX_SHIFT); + value |= _objc_obfuscatedTagToBasicTag(basicTag) << _OBJC_TAG_INDEX_SHIFT; +#endif + return value; +} + +static inline void * +_objc_encodeTaggedPointer(uintptr_t ptr) +{ + return _objc_encodeTaggedPointer_withObfuscator(ptr, objc_debug_taggedpointer_obfuscator); +} + +static inline uintptr_t +_objc_decodeTaggedPointer_noPermute(const void *ptr) +{ + return _objc_decodeTaggedPointer_noPermute_withObfuscator(ptr, objc_debug_taggedpointer_obfuscator); +} + +static inline uintptr_t +_objc_decodeTaggedPointer(const void *ptr) +{ + return _objc_decodeTaggedPointer_withObfuscator(ptr, objc_debug_taggedpointer_obfuscator); +} + +static inline bool +_objc_taggedPointersEnabled(void) +{ + extern uintptr_t objc_debug_taggedpointer_mask; + return (objc_debug_taggedpointer_mask != 0); +} + +__attribute__((no_sanitize("unsigned-shift-base"))) +static inline void * +_objc_makeTaggedPointer_withObfuscator(objc_tag_index_t tag, uintptr_t value, + uintptr_t obfuscator) +{ + // PAYLOAD_LSHIFT and PAYLOAD_RSHIFT are the payload extraction shifts. + // They are reversed here for payload insertion. + + // ASSERT(_objc_taggedPointersEnabled()); + if (tag <= OBJC_TAG_Last60BitPayload) { + // ASSERT(((value << _OBJC_TAG_PAYLOAD_RSHIFT) >> _OBJC_TAG_PAYLOAD_LSHIFT) == value); + uintptr_t result = + (_OBJC_TAG_MASK | + ((uintptr_t)tag << _OBJC_TAG_INDEX_SHIFT) | + ((value << _OBJC_TAG_PAYLOAD_RSHIFT) >> _OBJC_TAG_PAYLOAD_LSHIFT)); + return _objc_encodeTaggedPointer_withObfuscator(result, obfuscator); + } else { + // ASSERT(tag >= OBJC_TAG_First52BitPayload); + // ASSERT(tag <= OBJC_TAG_Last52BitPayload); + // ASSERT(((value << _OBJC_TAG_EXT_PAYLOAD_RSHIFT) >> _OBJC_TAG_EXT_PAYLOAD_LSHIFT) == value); + uintptr_t result = + (_OBJC_TAG_EXT_MASK | + ((uintptr_t)(tag - OBJC_TAG_First52BitPayload) << _OBJC_TAG_EXT_INDEX_SHIFT) | + ((value << _OBJC_TAG_EXT_PAYLOAD_RSHIFT) >> _OBJC_TAG_EXT_PAYLOAD_LSHIFT)); + return _objc_encodeTaggedPointer_withObfuscator(result, obfuscator); + } +} + +static inline void * +_objc_makeTaggedPointer(objc_tag_index_t tag, uintptr_t value) +{ + return _objc_makeTaggedPointer_withObfuscator(tag, value, objc_debug_taggedpointer_obfuscator); +} + +static inline bool +_objc_isTaggedPointer(const void *ptr) +{ + return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK; +} + +static inline bool +_objc_isTaggedPointerOrNil(const void *ptr) +{ + // this function is here so that clang can turn this into + // a comparison with NULL when this is appropriate + // it turns out it's not able to in many cases without this + return !ptr || ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK; +} + +static inline objc_tag_index_t +_objc_getTaggedPointerTag_withObfuscator(const void *ptr, + uintptr_t obfuscator) +{ + // ASSERT(_objc_isTaggedPointer(ptr)); + uintptr_t value = _objc_decodeTaggedPointer_withObfuscator(ptr, obfuscator); + uintptr_t basicTag = (value >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK; + uintptr_t extTag = (value >> _OBJC_TAG_EXT_INDEX_SHIFT) & _OBJC_TAG_EXT_INDEX_MASK; + if (basicTag == _OBJC_TAG_INDEX_MASK) { + return (objc_tag_index_t)(extTag + OBJC_TAG_First52BitPayload); + } else { + return (objc_tag_index_t)basicTag; + } +} + +__attribute__((no_sanitize("unsigned-shift-base"))) +static inline uintptr_t +_objc_getTaggedPointerValue_withObfuscator(const void *ptr, + uintptr_t obfuscator) +{ + // ASSERT(_objc_isTaggedPointer(ptr)); + uintptr_t value = _objc_decodeTaggedPointer_noPermute_withObfuscator(ptr, obfuscator); + uintptr_t basicTag = (value >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK; + if (basicTag == _OBJC_TAG_INDEX_MASK) { + return (value << _OBJC_TAG_EXT_PAYLOAD_LSHIFT) >> _OBJC_TAG_EXT_PAYLOAD_RSHIFT; + } else { + return (value << _OBJC_TAG_PAYLOAD_LSHIFT) >> _OBJC_TAG_PAYLOAD_RSHIFT; + } +} + +__attribute__((no_sanitize("unsigned-shift-base"))) +static inline intptr_t +_objc_getTaggedPointerSignedValue_withObfuscator(const void *ptr, + uintptr_t obfuscator) +{ + // ASSERT(_objc_isTaggedPointer(ptr)); + uintptr_t value = _objc_decodeTaggedPointer_noPermute_withObfuscator(ptr, obfuscator); + uintptr_t basicTag = (value >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK; + if (basicTag == _OBJC_TAG_INDEX_MASK) { + return ((intptr_t)value << _OBJC_TAG_EXT_PAYLOAD_LSHIFT) >> _OBJC_TAG_EXT_PAYLOAD_RSHIFT; + } else { + return ((intptr_t)value << _OBJC_TAG_PAYLOAD_LSHIFT) >> _OBJC_TAG_PAYLOAD_RSHIFT; + } +} + +static inline objc_tag_index_t +_objc_getTaggedPointerTag(const void *ptr) +{ + return _objc_getTaggedPointerTag_withObfuscator(ptr, objc_debug_taggedpointer_obfuscator); +} + +static inline uintptr_t +_objc_getTaggedPointerValue(const void *ptr) +{ + return _objc_getTaggedPointerValue_withObfuscator(ptr, objc_debug_taggedpointer_obfuscator); +} + +static inline intptr_t +_objc_getTaggedPointerSignedValue(const void *ptr) +{ + return _objc_getTaggedPointerSignedValue_withObfuscator(ptr, objc_debug_taggedpointer_obfuscator); +} + +# if OBJC_SPLIT_TAGGED_POINTERS +static inline void * +_objc_getTaggedPointerRawPointerValue(const void *ptr) { + return (void *)((uintptr_t)ptr & _OBJC_TAG_CONSTANT_POINTER_MASK); +} +# endif + +#else + +// Just check for nil when we don't support tagged pointers. +static inline bool +_objc_isTaggedPointerOrNil(const void *ptr) +{ + return !ptr; +} + +// OBJC_HAVE_TAGGED_POINTERS +#endif // ====================================================================== #pragma mark - objc4-680/runtime/objc-os.h - diff --git a/Tests/KSCrashRecordingCoreTests/KSObjC_Tests.m b/Tests/KSCrashRecordingCoreTests/KSObjC_Tests.m index 4a03018ab..e81ee44b6 100644 --- a/Tests/KSCrashRecordingCoreTests/KSObjC_Tests.m +++ b/Tests/KSCrashRecordingCoreTests/KSObjC_Tests.m @@ -735,7 +735,6 @@ - (void) testArrayDescriptionEmpty - (void) testArrayDescription { -#if 0 NSArray* array = [NSArray arrayWithObjects:@"test", nil]; void* arrayPtr = (__bridge void*)array; NSString* expectedClassName = [NSString stringWithCString:class_getName([array class]) encoding:NSUTF8StringEncoding]; @@ -758,7 +757,6 @@ - (void) testArrayDescription expectedClassName = [NSString stringWithCString:class_getName([expectedTheRest class]) encoding:NSUTF8StringEncoding]; XCTAssertEqualObjects(className, expectedClassName, @""); XCTAssertEqualObjects(theRest, expectedTheRest, @""); -#endif } - (void) testCopyArrayContentsImmutable