Skip to content
Permalink
Browse files

Merge pull request #43 from microsoft/fix/nonptr-isa

Apply bit mask for non-pointer isa values on macOS x64
  • Loading branch information
MatkovIvan committed Jan 10, 2020
2 parents e4cce15 + 6b8ccc6 commit 7ac89f021aa9f83b3bcf7ff6e969936068125251
@@ -6,6 +6,7 @@
* Remove `UIKit` dependency on iOS.
* Fix arm64e crash report text formatting.
* Fix possible crash `plcrash_log_writer_set_exception` method when `NSException` instances have a `nil` reason.
* Apply bit mask for non-pointer isa values on macOS x64 (used in runtime symbolication).

___

@@ -22,5 +23,5 @@ ___
* Support for arm64e devices that run an arm64 slice (which is the default for apps that were compiled with Xcode 10 or earlier).
* Remove support for armv6 CPU architecture as it is no longer supported.
* Improve namespacing to avoid symbol collisions when integrating PLCrashReporter.
* Fix a crash that occurred on macOS where PLCrashReporter would be caught in an endless loop handling signals.
* Fix a crash that occurred on macOS where PLCrashReporter would be caught in an endless loop handling signals.
* Make it possible to not add an uncaught exception handler via `shouldRegisterUncaughtExceptionHandler` property on `PLCrashReporterConfig`. This scenario is important when using PLCrashReporter inside managed runtimes, i.e. for a Xamarin app. This is not a breaking change and behavior will not change if you use PLCrashReporter.
@@ -42,14 +42,6 @@ extern "C" {
* @{
*/

/**
* @internal
* Flag set for non-ptr ISAs. This flag is not ABI stable, and may change.
*/
#define PLCRASH_ASYNC_OBJC_ISA_NONPTR_FLAG 0x1

extern const uint64_t PLCRASH_ASYNC_OBJC_ISA_NONPTR_CLASS_MASK;

/**
* @internal
*
@@ -107,8 +99,7 @@ typedef struct plcrash_async_objc_cache {

plcrash_error_t plcrash_async_objc_cache_init (plcrash_async_objc_cache_t *context);
void plcrash_async_objc_cache_free (plcrash_async_objc_cache_t *context);

bool plcrash_async_objc_supports_nonptr_isa (cpu_type_t type);
pl_vm_address_t plcrash_async_objc_isa_pointer (pl_vm_address_t isa);

/**
* A callback to invoke when an Objective-C method is found.
@@ -41,15 +41,6 @@
* @{
*/

/**
* @internal
* Executed in static initializers to determine whether the host uses the iOS 9+ ABI.
*/
static bool plcrash_async_image_objc_has_ios9_abi () {
NSProcessInfo *processInfo = [NSProcessInfo processInfo];
return TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR && processInfo.operatingSystemVersion.majorVersion >= 9;
};

static const char * const kObjCSegmentName = "__OBJC";
static const char * const kDataSegmentName = "__DATA";

@@ -62,22 +53,53 @@ static bool plcrash_async_image_objc_has_ios9_abi () {
static uint32_t CLS_NO_METHOD_ARRAY = 0x4000;
static uint32_t END_OF_METHODS_LIST = -1;

/**
* @internal
* Flag set for non-ptr ISAs. This flag is not ABI stable, and may change.
*/
#define PLCRASH_ASYNC_OBJC_ISA_NONPTR_FLAG 0x1

/**
* Return true if the given CPU uses non-pointer isa values.
*
* On x64 architectures, isa pointers are masked to
* allow for refcounting and maintaining bit flags when the LSB is 0x1.
*
* @warning ISA tagging is handled entirely in libobjc, and could be changed in any future release. There
* are variables vended from libobjc that return the isa pointer mask; we validate these in our
* tests, but we can't validate the meaning of bitfield checked here.
*
* The tagged isa pointers seem to be used even within the writable class data; as such, we must
* perform masking here, as well. This is another reason we should migrate the code to
* work directly on the backing unmodified pages, as that provides us with a stable ABI. In
* the worst case scenario, we'll simply fail to symbolicate a class should the ABI
* change incompatibly.
*
* @sa http://www.sealiesoftware.com/blog/archive/2013/09/24/objc_explain_Non-pointer_isa.html
* @sa https://github.com/opensource-apple/objc4/blob/master/runtime/objc-config.h
*/
#if !__LP64__ || TARGET_IPHONE_SIMULATOR
#define PLCRASH_ASYNC_OBJC_SUPPORT_NONPTR_ISA 0
#else
#define PLCRASH_ASYNC_OBJC_SUPPORT_NONPTR_ISA 1
#endif

/**
* @internal
* The pointer mask for non-pointer ISAs. This flag is not ABI stable, and may change; it is validated
* at development time via our unit tests. As per our API invariants, if this flag becomes out-of-sync
* with the host OS, our symbolication implementation will either fail to find some symbols, or will
* return incorrect symbols, but it will not crash.
*
* @sa https://github.com/opensource-apple/objc4/blob/master/runtime/objc-private.h
*/
const uint64_t PLCRASH_ASYNC_OBJC_ISA_NONPTR_CLASS_MASK = plcrash_async_image_objc_has_ios9_abi() ? 0xffffffff8ULL : 0x1fffffff8ULL;

/* TAGGED_ISA() returns the pointer value for a non-pointer isa. This assumes that the lsb flag of 0x1 will continue to be
* used to designate a non-pointer isa; see the plcrash_async_objc_supports_nonptr_isa documentation for more details */
#define TAGGED_ISA(img, isa) (\
plcrash_async_objc_supports_nonptr_isa(plcrash_async_macho_cpu_type(img)) && \
(isa & PLCRASH_ASYNC_OBJC_ISA_NONPTR_FLAG) ? \
(isa & PLCRASH_ASYNC_OBJC_ISA_NONPTR_CLASS_MASK) : \
(isa))
#if defined(__arm64__)
#define PLCRASH_ASYNC_OBJC_ISA_NONPTR_CLASS_MASK 0x0000000ffffffff8ULL
#elif defined(__x86_64__)
#define PLCRASH_ASYNC_OBJC_ISA_NONPTR_CLASS_MASK 0x00007ffffffffff8ULL
#else
#define PLCRASH_ASYNC_OBJC_ISA_NONPTR_CLASS_MASK ~1UL
#endif

/**
* @internal
@@ -602,7 +624,7 @@ static plcrash_error_t pl_async_objc_parse_from_module_info (plcrash_async_macho
}

/* Read a class structure for the metaclass. */
pl_vm_address_t isa = image->byteorder->swap32(cls.isa);
pl_vm_address_t isa = plcrash_async_objc_isa_pointer(image->byteorder->swap(cls.isa));
struct pl_objc1_class metaclass;
err = plcrash_async_task_memcpy(image->task, isa, 0, &metaclass, sizeof(metaclass));
if (err != PLCRASH_ESUCCESS) {
@@ -971,7 +993,7 @@ static plcrash_error_t pl_async_objc_parse_from_data_section (plcrash_async_mach
}

/* Read an architecture-appropriate class structure for the metaclass. */
pl_vm_address_t isa = TAGGED_ISA(image, image->byteorder->swap(classPtr->isa));
pl_vm_address_t isa = plcrash_async_objc_isa_pointer(image->byteorder->swap(classPtr->isa));
class_t *metaclass = (class_t *) plcrash_async_mobject_remap_address(&objcContext->objcDataMobj, isa, 0, sizeof(*metaclass));
if (metaclass == NULL) {
PLCF_DEBUG("plcrash_async_mobject_remap_address in objcDataMobj for pointer %llx returned NULL", (long long)isa);
@@ -1025,36 +1047,23 @@ static plcrash_error_t pl_async_objc_parse_from_data_section (plcrash_async_mach
}

/**
* Return true if the given CPU @a type uses non-pointer isa values.
*
* On ARM64 (and possibly future architectures), isa pointers are masked to
* allow for refcounting and maintaining bit flags when the LSB is 0x1.
*
* @param type A Mach-O CPU type, eg, the CPU type from a Mach-O image's header.
*
* @warning ISA tagging is handled entirely in libobjc, and could be changed in any future release. There
* are variables vended from libobjc that return the isa pointer mask; we validate these in our
* tests, but we can't validate the meaning of bitfield checked here.
*
* The tagged isa pointers seem to be used even within the writable class data; as such, we must
* perform masking here, as well. This is another reason we should migrate the code to
* work directly on the backing unmodified pages, as that provides us with a stable ABI. In
* the worst case scenario, we'll simply fail to symbolicate a class should the ABI
* change incompatibly.
*
* @sa http://www.sealiesoftware.com/blog/archive/2013/09/24/objc_explain_Non-pointer_isa.html
* Returns the pointer value for a non-pointer isa. This assumes that the lsb flag of 0x1 will continue to be
* used to designate a non-pointer isa; see the PLCRASH_ASYNC_OBJC_SUPPORT_NONPTR_ISA documentation for more details.
*/
bool plcrash_async_objc_supports_nonptr_isa (cpu_type_t type) {
/* Handle known architectures; we use a whilelist here to force implementors to evaluate new architectures
* during porting. */
#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__arm64__)
if (type == CPU_TYPE_ARM64)
return true;

return false;
#else
#error Add architecture definition.
pl_vm_address_t plcrash_async_objc_isa_pointer (pl_vm_address_t isa) {
#if PLCRASH_ASYNC_OBJC_SUPPORT_NONPTR_ISA
if (isa & PLCRASH_ASYNC_OBJC_ISA_NONPTR_FLAG) {
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
/* Before iOS 9 other bit-mask was used. */
NSProcessInfo *processInfo = [NSProcessInfo processInfo];
if (processInfo.operatingSystemVersion.majorVersion < 9) {
return isa & 0x00000001fffffff8UL;
}
#endif
return isa & PLCRASH_ASYNC_OBJC_ISA_NONPTR_CLASS_MASK;
}
#endif
return isa;
}

/**
@@ -117,26 +117,6 @@ static void ParseCallbackTrampoline(bool isClassMethod, plcrash_async_macho_stri
block(isClassMethod, className, methodName, imp);
}

/**
* Verify our fixed ISA mask against the non-stable objc_debug_isa_class_mask.
* Refer to plcrash_async_objc_supports_nonptr_isa and http://www.sealiesoftware.com/blog/archive/2013/09/24/objc_explain_Non-pointer_isa.html
* for more details.
*/
- (void) testVerifyISAClassMask {
#ifdef __arm64__
extern uint64_t objc_debug_isa_class_mask WEAK_IMPORT_ATTRIBUTE;

/* Skip hosts where non-ptr isas are not supported */
if (!plcrash_async_objc_supports_nonptr_isa(plcrash_async_macho_cpu_type(&_image)))
return;

STAssertNotNULL(objc_debug_isa_class_mask, @"The objc_debug_isa_class_mask variable is no longer vended");
STAssertEquals(objc_debug_isa_class_mask, PLCRASH_ASYNC_OBJC_ISA_NONPTR_CLASS_MASK, @"Incorrect class mask");
#else
STAssertFalse(plcrash_async_objc_supports_nonptr_isa(plcrash_async_macho_cpu_type(&_image)), @"This platform is declared to support non-ptr ISAs; the above tests should be enabled");
#endif
}

- (void) testParse {
__block plcrash_error_t err;

@@ -104,7 +104,6 @@
* building the library, and executing the following:
* nm -g -U <static library> | grep '^[0-9]' | c++filt | grep -v AcmeCo | grep -E '_pl|_PL' | awk '{print $3}' | cut -c 2- | sort | uniq | awk '{print "#define",$1,"PLNS("$1")"}'
*/
#define PLCRASH_ASYNC_OBJC_ISA_NONPTR_CLASS_MASK PLNS(PLCRASH_ASYNC_OBJC_ISA_NONPTR_CLASS_MASK)
#define pl_mach_thread_self PLNS(pl_mach_thread_self)
#define plcrash__architecture__descriptor PLNS(plcrash__architecture__descriptor)
#define plcrash__architecture__enum_values_by_name PLNS(plcrash__architecture__enum_values_by_name)
@@ -205,7 +204,7 @@
#define plcrash_async_objc_cache_free PLNS(plcrash_async_objc_cache_free)
#define plcrash_async_objc_cache_init PLNS(plcrash_async_objc_cache_init)
#define plcrash_async_objc_find_method PLNS(plcrash_async_objc_find_method)
#define plcrash_async_objc_supports_nonptr_isa PLNS(plcrash_async_objc_supports_nonptr_isa)
#define plcrash_async_objc_isa_pointer PLNS(plcrash_async_objc_isa_pointer)
#define plcrash_async_read_addr PLNS(plcrash_async_read_addr)
#define plcrash_async_signal_sigcode PLNS(plcrash_async_signal_sigcode)
#define plcrash_async_signal_signame PLNS(plcrash_async_signal_signame)

0 comments on commit 7ac89f0

Please sign in to comment.
You can’t perform that action at this time.