in Source/PLCrashAsyncObjCSection.mm [824:913]
static plcrash_error_t pl_async_objc_parse_objc2_class (plcrash_async_macho_t *image, plcrash_async_objc_cache_t *objc_cache, class_t *cls, plcrash_async_macho_string_t *class_name, class_ro_t *cls_data_ro) {
plcrash_error_t err;
/* Grab the class's data_rw pointer. This needs masking because it also
* can contain flags. */
pl_vm_address_t data_ptr = (pl_vm_address_t) image->byteorder->swap(cls->data_rw);
data_ptr &= FAST_DATA_MASK;
/* Grab the data RO pointer from the cache. If unavailable, we'll fetch the data and populate the class. */
pl_vm_address_t cached_data_ro_addr = cache_lookup(objc_cache, data_ptr);
if (cached_data_ro_addr == 0) {
class_rw_t cls_data_rw;
/* Read the class_rw structure. */
err = plcrash_async_task_memcpy(image->task, data_ptr, 0, &cls_data_rw, sizeof(cls_data_rw));
if (err != PLCRASH_ESUCCESS) {
PLCF_DEBUG("plcrash_async_task_memcpy at 0x%llx error %d", (long long)data_ptr, err);
return PLCRASH_EINVALID_DATA;
}
/* Check the flags. If it's not yet realized, then we need to skip the class. */
uint32_t flags = image->byteorder->swap(cls_data_rw.flags);
if ((flags & RW_REALIZED) == 0) {
// PLCF_DEBUG("Found unrealized class with RO data at 0x%llx, skipping it", (long long)data_ptr);
return PLCRASH_ENOTFOUND;
}
/* Grab the data_ro pointer. The RO data (read-only) contains the class name
* and method list. */
cached_data_ro_addr = (pl_vm_address_t) image->byteorder->swap(cls_data_rw.data_ro);
/* When objc_class_abi_version >= 1, it's a tagged union based on the low bit:
* 0: class_ro_t 1: class_rw_ext_t
* @see https://opensource.apple.com/source/objc4/objc4-781/runtime/objc-runtime-new.h */
if (cached_data_ro_addr & 0x1) {
cached_data_ro_addr &= ~0x1;
/* class_rw_ext_t has a link to R/O data without any offset, so just grab this pointer. */
machine_ptr_t data_ro_addr;
err = plcrash_async_task_memcpy(image->task, cached_data_ro_addr, 0, &data_ro_addr, sizeof(data_ro_addr));
if (err != PLCRASH_ESUCCESS) {
PLCF_DEBUG("plcrash_async_task_memcpy at 0x%llx error %d", (long long)cached_data_ro_addr, err);
return PLCRASH_EINVALID_DATA;
}
cached_data_ro_addr = (pl_vm_address_t) image->byteorder->swap(data_ro_addr);
}
/* Copy the R/O data; it will either be heap allocated (RW_COPIED_RO), found within the __objc_const section of the current cached image,
* or found within the __objc_const section of a non-cached image. */
if ((flags & RW_COPIED_RO) != 0 || !plcrash_async_macho_contains_address(image, cached_data_ro_addr)) {
if ((err = plcrash_async_task_memcpy(image->task, cached_data_ro_addr, 0, cls_data_ro, sizeof(*cls_data_ro))) != PLCRASH_ESUCCESS) {
PLCF_DEBUG("plcrash_async_task_memcpy at 0x%llx returned NULL", (long long)cached_data_ro_addr);
return PLCRASH_EINVALID_DATA;
}
} else {
void *ptr = plcrash_async_mobject_remap_address(&objc_cache->objcConstMobj, cached_data_ro_addr, 0, sizeof(*cls_data_ro));
if (ptr == NULL) {
PLCF_DEBUG("plcrash_async_mobject_remap_address in __objc_const for pointer 0x%llx returned NULL", (long long)cached_data_ro_addr);
return PLCRASH_EINVALID_DATA;
}
plcrash_async_memcpy(cls_data_ro, ptr, sizeof(*cls_data_ro));
}
/* Add a new cache entry. */
cache_set(objc_cache, data_ptr, cached_data_ro_addr);
} else {
void *ptr;
/* We know that the address is valid (it wouldn't be in the cache otherwise). We try the cheaper memory mapping first,
* and then fall back to a memory copy. */
if ((ptr = plcrash_async_mobject_remap_address(&objc_cache->objcConstMobj, cached_data_ro_addr, 0, sizeof(*cls_data_ro))) != NULL) {
plcrash_async_memcpy(cls_data_ro, ptr, sizeof(*cls_data_ro));
} else if (plcrash_async_task_memcpy(image->task, cached_data_ro_addr, 0, cls_data_ro, sizeof(*cls_data_ro)) == PLCRASH_ESUCCESS) {
/* Do nothing -- success! */
} else {
PLCF_DEBUG("Failed to read validated class_ro data at 0x%llx", (long long)cached_data_ro_addr);
return PLCRASH_EINVALID_DATA;
}
}
/* Fetch the pointer to the class name, and make the string. */
pl_vm_address_t class_name_ptr = (pl_vm_address_t) image->byteorder->swap(cls_data_ro->name);
err = plcrash_async_macho_string_init(class_name, image, class_name_ptr);
if (err != PLCRASH_ESUCCESS) {
PLCF_DEBUG("plcrash_async_macho_string_init at 0x%llx error %d", (long long)class_name_ptr, err);
return PLCRASH_EINVALID_DATA;
}
return err;
}