static plcrash_error_t pl_async_objc_parse_objc2_method_list()

in Source/PLCrashAsyncObjCSection.mm [729:803]


static plcrash_error_t pl_async_objc_parse_objc2_method_list (plcrash_async_macho_t *image,
                                                              plcrash_async_objc_cache_t *objc_cache,
                                                              plcrash_async_macho_string_t *class_name,
                                                              bool is_meta_class,
                                                              pl_vm_address_t method_list_addr,
                                                              plcrash_async_objc_found_method_cb callback,
                                                              void *ctx)
{
    PLCF_ASSERT(method_list_addr != 0);
    
    /* Read the method list header. */
    struct pl_objc2_list_header *header;
    plcrash_async_mobject_t *section = &objc_cache->objcConstMobj;
    header = (struct pl_objc2_list_header *) plcrash_async_mobject_remap_address(section, method_list_addr, 0, sizeof(*header));
    if (header == NULL && objc_cache->objcConstAxMobjInitialized) {
        section = &objc_cache->objcConstAxMobj;
        header = (struct pl_objc2_list_header *) plcrash_async_mobject_remap_address(section, method_list_addr, 0, sizeof(*header));
    }
    if (header == NULL) {
        PLCF_DEBUG("plcrash_async_mobject_remap_address in __objc_const and __objc_const_ax failed to map methods pointer 0x%llx", (long long) method_list_addr);
        return PLCRASH_EINVALID_DATA;
    }
    
    /* Extract the entry size and count from the list header. */
    uint32_t entsize = image->byteorder->swap32(header->entsize) & ~(uint32_t)3;
    uint32_t count = image->byteorder->swap32(header->count);
    
    /* Compute the method list start position and length. */
    pl_vm_address_t method_list_start = method_list_addr + sizeof(*header);
    pl_vm_size_t method_list_length = (pl_vm_size_t)entsize * count;
    
    const char *cursor = (const char *) plcrash_async_mobject_remap_address(section, method_list_start, 0, method_list_length);
    if (cursor == NULL) {
        PLCF_DEBUG("plcrash_async_mobject_remap_address at 0x%llx length %llu returned NULL", (long long)method_list_start, (unsigned long long)method_list_length);
        return PLCRASH_EINVALID_DATA;
    }
    
    /* Extract methods from the list. */
    for (uint32_t i = 0; i < count; i++) {
        plcrash_error_t err;

        /* Read an architecture-appropriate method structure from the
         * current cursor. */
        const struct pl_objc2_method_32 *method_32 = (const struct pl_objc2_method_32 *)cursor;
        const struct pl_objc2_method_64 *method_64 = (const struct pl_objc2_method_64 *)cursor;
        
        /* Extract the method name pointer. */
        pl_vm_address_t methodNamePtr = (image->m64
                                         ? (pl_vm_address_t) image->byteorder->swap64(method_64->name)
                                         : image->byteorder->swap32(method_32->name));
        
        /* Read the method name. */
        plcrash_async_macho_string_t method_name;
        if ((err = plcrash_async_macho_string_init(&method_name, image, methodNamePtr)) != PLCRASH_ESUCCESS) {
            PLCF_DEBUG("plcrash_async_macho_string_init at 0x%llx error %d", (long long)methodNamePtr, err);
            return err;
        }
    
        /* Extract the method IMP. */
        pl_vm_address_t imp = (image->m64
                               ? (pl_vm_address_t) image->byteorder->swap64(method_64->imp)
                               : image->byteorder->swap32(method_32->imp));
        
        /* Call the callback. */
        callback(is_meta_class, class_name, &method_name, imp, ctx);

        /* Clean up the method name. */
        plcrash_async_macho_string_free(&method_name);
        
        /* Increment the cursor by the entry size for the next iteration of the loop. */
        cursor += entsize;
    }

    return PLCRASH_ESUCCESS;
}