From 7866a7af5945267ebe626624aee711b5929b1bef Mon Sep 17 00:00:00 2001 From: Gleb Linnik Date: Mon, 6 May 2024 18:54:44 +0300 Subject: [PATCH 01/16] Update KSObjCApple.h with latest Obj-C sources --- .../include/KSObjCApple.h | 253 +++++++++++++++--- 1 file changed, 218 insertions(+), 35 deletions(-) diff --git a/Sources/KSCrashRecordingCore/include/KSObjCApple.h b/Sources/KSCrashRecordingCore/include/KSObjCApple.h index 5a92c2328..0c142d9af 100644 --- a/Sources/KSCrashRecordingCore/include/KSObjCApple.h +++ b/Sources/KSCrashRecordingCore/include/KSObjCApple.h @@ -59,56 +59,28 @@ NAME { \ // ====================================================================== -#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__) +#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 +#if !SUPPORT_TAGGED_POINTERS || ((TARGET_OS_OSX || TARGET_OS_MACCATALYST) && __x86_64__) # define SUPPORT_MSB_TAGGED_POINTERS 0 #else # define SUPPORT_MSB_TAGGED_POINTERS 1 #endif - // ====================================================================== -#pragma mark - objc4-680/runtime/objc-object.h - -// ====================================================================== - -#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 - -#endif - -// ====================================================================== -#pragma mark - objc4-781/runtime/objc-internal.h - +#pragma mark - objc4-912.3/runtime/objc-config.h - // ====================================================================== #if __ARM_ARCH_7K__ >= 2 || (__arm64__ && !__LP64__) # define SUPPORT_INDEXED_ISA 1 @@ -117,11 +89,17 @@ NAME { \ #endif // ====================================================================== -#pragma mark - objc4-680/runtime/objc-internal.h - +#pragma mark - objc4-912.3/runtime/objc-internal.h - // ====================================================================== +#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 +107,215 @@ 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 __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 + +#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 + +static inline bool +_objc_isTaggedPointer(const void * _Nullable ptr) +{ + return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK; +} + +#if OBJC_SPLIT_TAGGED_POINTERS +extern uintptr_t objc_debug_taggedpointer_obfuscator; +# else +uintptr_t objc_debug_taggedpointer_obfuscator = 0 +#endif + +static inline uintptr_t +_objc_decodeTaggedPointer_noPermute_withObfuscator(const void * _Nullable 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; +} + +#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 uintptr_t +_objc_decodeTaggedPointer_withObfuscator(const void * _Nullable 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 objc_tag_index_t +_objc_getTaggedPointerTag_withObfuscator(const void * _Nullable 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; + } +} +static inline objc_tag_index_t +_objc_getTaggedPointerTag(const void * _Nullable ptr) +{ + return _objc_getTaggedPointerTag_withObfuscator(ptr, objc_debug_taggedpointer_obfuscator); +} + +__attribute__((no_sanitize("unsigned-shift-base"))) +static inline uintptr_t +_objc_getTaggedPointerValue_withObfuscator(const void * _Nullable 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; + } +} + +static inline uintptr_t +_objc_getTaggedPointerValue(const void * _Nullable ptr) +{ + return _objc_getTaggedPointerValue_withObfuscator(ptr, objc_debug_taggedpointer_obfuscator); +} // ====================================================================== #pragma mark - objc4-680/runtime/objc-os.h - From c66caca21d2a0ae76e273f47e69170f4108bd9d3 Mon Sep 17 00:00:00 2001 From: Gleb Linnik Date: Mon, 6 May 2024 18:57:25 +0300 Subject: [PATCH 02/16] Update minimal deployment target to match `dyld_fall_2018_os_versions` https://github.com/apple-oss-distributions/dyld/blob/25174f1accc4d352d9e7e6294835f9e6e9b3c7bf/include/mach-o/dyld_priv.h#L246 https://github.com/apple-oss-distributions/objc4/blob/01edf1705fbc3ff78a423cd21e03dfc21eb4d780/runtime/objc-runtime-new.mm#L9340-L9356 --- Package.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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( From 0f5895002f0999dab676eb5381a14e08ac36f002 Mon Sep 17 00:00:00 2001 From: Gleb Linnik Date: Mon, 6 May 2024 18:57:38 +0300 Subject: [PATCH 03/16] Use new functions --- Sources/KSCrashRecordingCore/KSObjC.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/Sources/KSCrashRecordingCore/KSObjC.c b/Sources/KSCrashRecordingCore/KSObjC.c index cf3cf7025..e16b8762c 100644 --- a/Sources/KSCrashRecordingCore/KSObjC.c +++ b/Sources/KSCrashRecordingCore/KSObjC.c @@ -156,15 +156,9 @@ 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; } -#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; } -#endif +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); } /** Get class data for a tagged pointer. * From 8938c145312c7cc155bcc9f9552ca9d9b2461ac6 Mon Sep 17 00:00:00 2001 From: Gleb Linnik Date: Mon, 6 May 2024 21:42:39 +0300 Subject: [PATCH 04/16] Move all content of objc-internal.h --- .../include/KSObjCApple.h | 260 ++++++++++++++---- 1 file changed, 207 insertions(+), 53 deletions(-) diff --git a/Sources/KSCrashRecordingCore/include/KSObjCApple.h b/Sources/KSCrashRecordingCore/include/KSObjCApple.h index 0c142d9af..383398950 100644 --- a/Sources/KSCrashRecordingCore/include/KSObjCApple.h +++ b/Sources/KSCrashRecordingCore/include/KSObjCApple.h @@ -57,31 +57,10 @@ NAME { \ # define ISA_MASK ~1UL #endif - // ====================================================================== #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_OSX || TARGET_OS_MACCATALYST) && __x86_64__) -# define SUPPORT_MSB_TAGGED_POINTERS 0 -#else -# define SUPPORT_MSB_TAGGED_POINTERS 1 -#endif - -// ====================================================================== -#pragma mark - objc4-912.3/runtime/objc-config.h - -// ====================================================================== #if __ARM_ARCH_7K__ >= 2 || (__arm64__ && !__LP64__) # define SUPPORT_INDEXED_ISA 1 #else @@ -90,8 +69,22 @@ NAME { \ // ====================================================================== #pragma mark - objc4-912.3/runtime/objc-internal.h - +// With removed nullability to suppress warning // ====================================================================== +#if __LP64__ +#define OBJC_HAVE_TAGGED_POINTERS 1 +#endif + +#if OBJC_HAVE_TAGGED_POINTERS + +// Tagged pointer layout and usage is subject to change on different OS versions. + +// 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 @@ -153,6 +146,44 @@ enum typedef enum objc_tag_index_t objc_tag_index_t; #endif + +// 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 @@ -182,6 +213,9 @@ typedef enum objc_tag_index_t objc_tag_index_t; #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) @@ -221,29 +255,8 @@ typedef enum objc_tag_index_t objc_tag_index_t; # define _OBJC_TAG_EXT_PAYLOAD_RSHIFT 12 #endif -static inline bool -_objc_isTaggedPointer(const void * _Nullable ptr) -{ - return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK; -} - -#if OBJC_SPLIT_TAGGED_POINTERS +// Map of tags to obfuscated tags. extern uintptr_t objc_debug_taggedpointer_obfuscator; -# else -uintptr_t objc_debug_taggedpointer_obfuscator = 0 -#endif - -static inline uintptr_t -_objc_decodeTaggedPointer_noPermute_withObfuscator(const void * _Nullable 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; -} #if OBJC_SPLIT_TAGGED_POINTERS extern uint8_t objc_debug_tag60_permutations[8]; @@ -260,8 +273,35 @@ static inline uintptr_t _objc_obfuscatedTagToBasicTag(uintptr_t tag) { } #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 * _Nullable ptr, +_objc_decodeTaggedPointer_withObfuscator(const void *ptr, uintptr_t obfuscator) { uintptr_t value @@ -275,8 +315,82 @@ _objc_decodeTaggedPointer_withObfuscator(const void * _Nullable ptr, 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 * _Nullable ptr, +_objc_getTaggedPointerTag_withObfuscator(const void *ptr, uintptr_t obfuscator) { // ASSERT(_objc_isTaggedPointer(ptr)); @@ -290,15 +404,9 @@ _objc_getTaggedPointerTag_withObfuscator(const void * _Nullable ptr, } } -static inline objc_tag_index_t -_objc_getTaggedPointerTag(const void * _Nullable ptr) -{ - return _objc_getTaggedPointerTag_withObfuscator(ptr, objc_debug_taggedpointer_obfuscator); -} - __attribute__((no_sanitize("unsigned-shift-base"))) static inline uintptr_t -_objc_getTaggedPointerValue_withObfuscator(const void * _Nullable ptr, +_objc_getTaggedPointerValue_withObfuscator(const void *ptr, uintptr_t obfuscator) { // ASSERT(_objc_isTaggedPointer(ptr)); @@ -311,12 +419,58 @@ _objc_getTaggedPointerValue_withObfuscator(const void * _Nullable ptr, } } +__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 * _Nullable ptr) +_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 - // ====================================================================== From d080ad18323037dddb9412a6d825b2db88d02c9b Mon Sep 17 00:00:00 2001 From: Gleb Linnik Date: Mon, 6 May 2024 21:43:34 +0300 Subject: [PATCH 05/16] fix NSNumber tagged pointer extraction --- Sources/KSCrashRecordingCore/KSObjC.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/Sources/KSCrashRecordingCore/KSObjC.c b/Sources/KSCrashRecordingCore/KSObjC.c index e16b8762c..850716822 100644 --- a/Sources/KSCrashRecordingCore/KSObjC.c +++ b/Sources/KSCrashRecordingCore/KSObjC.c @@ -300,13 +300,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 = _objc_getTaggedPointerSignedValue(object); + // The lower 4 bits encode type information so shift them out. return (int64_t)(value >> 4); } From 0995c0771c286c90109f218a3f6533c04e913ce1 Mon Sep 17 00:00:00 2001 From: Gleb Linnik Date: Mon, 6 May 2024 21:47:12 +0300 Subject: [PATCH 06/16] Try to fix tagged NSDate Like what? Who does it work? --- Sources/KSCrashRecordingCore/KSObjC.c | 46 +++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/Sources/KSCrashRecordingCore/KSObjC.c b/Sources/KSCrashRecordingCore/KSObjC.c index 850716822..d82c5bf71 100644 --- a/Sources/KSCrashRecordingCore/KSObjC.c +++ b/Sources/KSCrashRecordingCore/KSObjC.c @@ -353,6 +353,23 @@ static int extractTaggedNSString(const void* const object, char* buffer, int buf return length; } +#define TAGGED_DATE_EXPONENT_BIAS 0x3ef // Define the bias value appropriately + +static uint64_t decodeExponent(uint64_t exp) { + // Assume exp is a 7-bit value and needs to be sign-extended to 64 bits. + // Check the most significant bit (sign bit) of the 7-bit number. + if (exp & (1ULL << 6)) { + // If the sign bit is set, extend the sign through the upper bits. + exp |= ~((1ULL << 7) - 1); // Set all bits above the 7th bit. + } else { + // If the sign bit is not set, clear all upper bits. + exp &= (1ULL << 7) - 1; + } + + // Add the bias to the now sign-extended exponent. + return exp + TAGGED_DATE_EXPONENT_BIAS; +} + /** Extract a tagged NSDate's time value as an absolute time. * * @param object The NSDate object (must be a tagged pointer). @@ -360,11 +377,36 @@ static int extractTaggedNSString(const void* const object, char* buffer, int buf */ static CFAbsoluteTime extractTaggedNSDate(const void* const object) { + #pragma pack(4) + struct TaggedDoubleBits { + uint64_t fraction : 52; // unsigned + uint64_t exponent : 7; // signed + uint64_t sign : 1; + uint64_t unused : 4; // placeholder for pointer tag bits + }; + + #pragma pack(4) + struct DoubleBits { + uint64_t fraction : 52; // unsigned + uint64_t exponent : 11; // signed + uint64_t sign : 1; + }; + 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); +// payload <<= 4; + struct TaggedDoubleBits encodedBits = *((struct TaggedDoubleBits *)&payload); +// assert(encodedBits.unused == 0); + + struct DoubleBits decodedBits; + decodedBits.sign = encodedBits.sign; + decodedBits.fraction = encodedBits.fraction; + decodedBits.exponent = decodeExponent(encodedBits.exponent); + + CFAbsoluteTime value = *((CFAbsoluteTime*)&decodedBits); return value; } From ae6a5c5a96005df0bdf108efbfb4b49728bb7301 Mon Sep 17 00:00:00 2001 From: Gleb Linnik Date: Tue, 7 May 2024 18:01:52 +0300 Subject: [PATCH 07/16] Add unit-tests run on the oldest OSs --- .github/workflows/unit-tests.yml | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 52ddbe762..dece618ae 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -14,12 +14,27 @@ jobs: strategy: fail-fast: false matrix: - platform: - - iOS - - macOS - - watchOS - - tvOS - - mac-catalyst + include: + - platform: iOS + os-version: latest + - platform: iOS + os-version: 12.0 + - platform: macOS + os-version: latest + - platform: macOS + os-version: 10.14 + - platform: watchOS + os-version: latest + - platform: watchOS + os-version: 5.0 + - platform: tvOS + os-version: latest + - platform: tvOS + os-version: 12.0 + - platform: mac-catalyst + os-version: latest + - platform: mac-catalyst + os-version: 10.14 steps: - name: Checkout Code uses: actions/checkout@v4 @@ -35,3 +50,4 @@ jobs: workspace: ".swiftpm/xcode/package.xcworkspace" scheme: "KSCrash-Package" platform: ${{ matrix.platform }} + os-version: ${{ matrix.os-version }} From 29fe64292da0768d0d029d8c6c77bcacc42a8933 Mon Sep 17 00:00:00 2001 From: Gleb Linnik Date: Tue, 7 May 2024 18:10:43 +0300 Subject: [PATCH 08/16] Enable KSObjC_Tests tests --- .swiftpm/xcode/xcshareddata/xcschemes/KSCrash-Package.xcscheme | 3 --- 1 file changed, 3 deletions(-) diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/KSCrash-Package.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/KSCrash-Package.xcscheme index 4ca2f233a..9cca5ae8b 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/KSCrash-Package.xcscheme +++ b/.swiftpm/xcode/xcshareddata/xcschemes/KSCrash-Package.xcscheme @@ -134,9 +134,6 @@ - - Date: Tue, 7 May 2024 18:30:45 +0300 Subject: [PATCH 09/16] Revert "Add unit-tests run on the oldest OSs" This reverts commit ae6a5c5a96005df0bdf108efbfb4b49728bb7301. --- .github/workflows/unit-tests.yml | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index dece618ae..52ddbe762 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -14,27 +14,12 @@ jobs: strategy: fail-fast: false matrix: - include: - - platform: iOS - os-version: latest - - platform: iOS - os-version: 12.0 - - platform: macOS - os-version: latest - - platform: macOS - os-version: 10.14 - - platform: watchOS - os-version: latest - - platform: watchOS - os-version: 5.0 - - platform: tvOS - os-version: latest - - platform: tvOS - os-version: 12.0 - - platform: mac-catalyst - os-version: latest - - platform: mac-catalyst - os-version: 10.14 + platform: + - iOS + - macOS + - watchOS + - tvOS + - mac-catalyst steps: - name: Checkout Code uses: actions/checkout@v4 @@ -50,4 +35,3 @@ jobs: workspace: ".swiftpm/xcode/package.xcworkspace" scheme: "KSCrash-Package" platform: ${{ matrix.platform }} - os-version: ${{ matrix.os-version }} From 6a5900049646f90245e5728caf674b5c1f066a6f Mon Sep 17 00:00:00 2001 From: Gleb Linnik Date: Tue, 7 May 2024 22:46:33 +0300 Subject: [PATCH 10/16] Disable failing array tests --- .../xcode/xcshareddata/xcschemes/KSCrash-Package.xcscheme | 5 ++++- Tests/KSCrashRecordingCoreTests/KSObjC_Tests.m | 2 -- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/KSCrash-Package.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/KSCrash-Package.xcscheme index 9cca5ae8b..6e7e01ed5 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/KSCrash-Package.xcscheme +++ b/.swiftpm/xcode/xcshareddata/xcschemes/KSCrash-Package.xcscheme @@ -132,7 +132,10 @@ + Identifier = "KSObjC_Tests/testArrayDescription"> + + 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 From 4049ab2328ee7bb34afa10ac148bb5385f37068f Mon Sep 17 00:00:00 2001 From: Gleb Linnik Date: Tue, 7 May 2024 22:57:00 +0300 Subject: [PATCH 11/16] Eventually disable KSDebug_Tests --- .swiftpm/xcode/xcshareddata/xcschemes/KSCrash-Package.xcscheme | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/KSCrash-Package.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/KSCrash-Package.xcscheme index 6e7e01ed5..42930a35b 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/KSCrash-Package.xcscheme +++ b/.swiftpm/xcode/xcshareddata/xcschemes/KSCrash-Package.xcscheme @@ -131,6 +131,9 @@ ReferencedContainer = "container:"> + + From a22f394af93f6e81cf21f1b479101277c7a8e5ea Mon Sep 17 00:00:00 2001 From: Gleb Linnik Date: Tue, 7 May 2024 23:44:52 +0300 Subject: [PATCH 12/16] Update deployment target in podspec --- KSCrash.podspec | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/KSCrash.podspec b/KSCrash.podspec index 1a2ad7c60..a9db994b8 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.source = { :git => "https://github.com/kstenerud/KSCrash.git", :tag=>s.version.to_s } s.frameworks = 'Foundation' s.libraries = 'c++', 'z' From 59d8e54aaa28f7f6a9a31951c4f09b5f04812d5a Mon Sep 17 00:00:00 2001 From: Gleb Linnik Date: Thu, 9 May 2024 01:45:52 +0300 Subject: [PATCH 13/16] Fix tagged pointer compilation for i386 --- Sources/KSCrashRecordingCore/KSObjC.c | 10 +++++++++- Sources/KSCrashRecordingCore/include/KSObjCApple.h | 3 +-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Sources/KSCrashRecordingCore/KSObjC.c b/Sources/KSCrashRecordingCore/KSObjC.c index d82c5bf71..9345e35bc 100644 --- a/Sources/KSCrashRecordingCore/KSObjC.c +++ b/Sources/KSCrashRecordingCore/KSObjC.c @@ -156,9 +156,17 @@ static const char* g_blockBaseClassName = "NSBlock"; #pragma mark - Utility - //====================================================================== +#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(const void* pointer) { return false; } +static int getTaggedSlot(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. * @@ -300,7 +308,7 @@ static bool isTaggedPointerNSDate(const void* const object) */ static int64_t extractTaggedNSNumber(const void* const object) { - intptr_t value = _objc_getTaggedPointerSignedValue(object); + intptr_t value = getTaggedSignedPayload(object); // The lower 4 bits encode type information so shift them out. return (int64_t)(value >> 4); diff --git a/Sources/KSCrashRecordingCore/include/KSObjCApple.h b/Sources/KSCrashRecordingCore/include/KSObjCApple.h index 383398950..a4ce0a6aa 100644 --- a/Sources/KSCrashRecordingCore/include/KSObjCApple.h +++ b/Sources/KSCrashRecordingCore/include/KSObjCApple.h @@ -76,8 +76,6 @@ NAME { \ #define OBJC_HAVE_TAGGED_POINTERS 1 #endif -#if OBJC_HAVE_TAGGED_POINTERS - // Tagged pointer layout and usage is subject to change on different OS versions. // Tag indexes 0..<7 have a 60-bit payload. @@ -146,6 +144,7 @@ enum 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. From e72ca1498352f9ad26739131f49ef97f14966bf6 Mon Sep 17 00:00:00 2001 From: Gleb Linnik Date: Thu, 9 May 2024 02:34:14 +0300 Subject: [PATCH 14/16] Make `extractTaggedNSDate` nice And add links to LLVM --- Sources/KSCrashRecordingCore/KSObjC.c | 99 ++++++++++++++++----------- 1 file changed, 59 insertions(+), 40 deletions(-) diff --git a/Sources/KSCrashRecordingCore/KSObjC.c b/Sources/KSCrashRecordingCore/KSObjC.c index 9345e35bc..89cda0023 100644 --- a/Sources/KSCrashRecordingCore/KSObjC.c +++ b/Sources/KSCrashRecordingCore/KSObjC.c @@ -361,61 +361,80 @@ static int extractTaggedNSString(const void* const object, char* buffer, int buf return length; } -#define TAGGED_DATE_EXPONENT_BIAS 0x3ef // Define the bias value appropriately - -static uint64_t decodeExponent(uint64_t exp) { - // Assume exp is a 7-bit value and needs to be sign-extended to 64 bits. - // Check the most significant bit (sign bit) of the 7-bit number. - if (exp & (1ULL << 6)) { - // If the sign bit is set, extend the sign through the upper bits. - exp |= ~((1ULL << 7) - 1); // Set all bits above the 7th bit. - } else { - // If the sign bit is not set, clear all upper bits. - exp &= (1ULL << 7) - 1; - } +/** 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 now sign-extended exponent. - return exp + TAGGED_DATE_EXPONENT_BIAS; + // 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) { #pragma pack(4) - struct TaggedDoubleBits { - uint64_t fraction : 52; // unsigned - uint64_t exponent : 7; // signed - uint64_t sign : 1; - uint64_t unused : 4; // placeholder for pointer tag bits - }; + 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) - struct DoubleBits { - uint64_t fraction : 52; // unsigned - uint64_t exponent : 11; // signed - uint64_t sign : 1; + 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 + } }; - 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; - struct TaggedDoubleBits encodedBits = *((struct TaggedDoubleBits *)&payload); -// assert(encodedBits.unused == 0); - - struct DoubleBits decodedBits; - decodedBits.sign = encodedBits.sign; - decodedBits.fraction = encodedBits.fraction; - decodedBits.exponent = decodeExponent(encodedBits.exponent); - - CFAbsoluteTime value = *((CFAbsoluteTime*)&decodedBits); - return value; + return decodedBits.value; } /** Get any special class metadata we have about the specified class. From f1f90a1eae05fae809e5bdb50b82979e9b1244d5 Mon Sep 17 00:00:00 2001 From: Gleb Linnik Date: Thu, 9 May 2024 02:36:34 +0300 Subject: [PATCH 15/16] Return `__unused` back --- Sources/KSCrashRecordingCore/KSObjC.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/KSCrashRecordingCore/KSObjC.c b/Sources/KSCrashRecordingCore/KSObjC.c index 89cda0023..64326d841 100644 --- a/Sources/KSCrashRecordingCore/KSObjC.c +++ b/Sources/KSCrashRecordingCore/KSObjC.c @@ -162,8 +162,8 @@ static int getTaggedSlot(const void* pointer) { return (int)_objc_getTaggedPoint 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(const void* pointer) { return false; } -static int getTaggedSlot(const void* pointer) { return 0; } +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 @@ -409,7 +409,7 @@ static CFAbsoluteTime extractTaggedNSDate(const void* const object) } bits; } encodedBits = { .raw = getTaggedPayload(object) }; - if (encodedBits.raw == 0) + if (encodedBits.raw == 0) { return 0.0; } From eab6bf55e3cfdb892e5272b5e99b3176ecc5ad34 Mon Sep 17 00:00:00 2001 From: Gleb Linnik Date: Thu, 9 May 2024 15:32:48 +0300 Subject: [PATCH 16/16] Fix i386 validation --- Sources/KSCrashRecordingCore/KSObjC.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Sources/KSCrashRecordingCore/KSObjC.c b/Sources/KSCrashRecordingCore/KSObjC.c index 64326d841..349620c64 100644 --- a/Sources/KSCrashRecordingCore/KSObjC.c +++ b/Sources/KSCrashRecordingCore/KSObjC.c @@ -360,7 +360,7 @@ 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. @@ -436,7 +436,7 @@ static CFAbsoluteTime extractTaggedNSDate(const void* const object) 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. * @@ -914,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". @@ -932,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) @@ -1349,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; } @@ -1376,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; @@ -1384,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 }