static plcrash_error_t pl_async_parse_obj1_class()

in Source/PLCrashAsyncObjCSection.mm [500:610]


static plcrash_error_t pl_async_parse_obj1_class(plcrash_async_macho_t *image, struct pl_objc1_class *cls, bool isMetaClass, plcrash_async_objc_found_method_cb callback, void *ctx) {
    plcrash_error_t err;
    
    /* Get the class's name. */
    pl_vm_address_t namePtr = image->byteorder->swap32(cls->name);
    bool classNameInitialized = false;
    plcrash_async_macho_string_t className;
    err = plcrash_async_macho_string_init(&className, image, namePtr);
    if (err != PLCRASH_ESUCCESS) {
        PLCF_DEBUG("plcrash_async_macho_string_init at 0x%llx error %d", (long long)namePtr, err);
        return err;
    }
    classNameInitialized = true;
    
    /* Grab the method list pointer. This is either a pointer to
     * a single method_list structure, OR a pointer to an array
     * of pointers to method_list structures, depending on the
     * flag in the .info field. Argh. */
    pl_vm_address_t methodListPtr = image->byteorder->swap32(cls->methods);
    
    /* If CLS_NO_METHOD_ARRAY is set, then methodListPtr points to
     * one method_list. If it's not set, then it points to an
     * array of pointers to method lists. */
    bool hasMultipleMethodLists = (image->byteorder->swap32(cls->info) & CLS_NO_METHOD_ARRAY) == 0;
    pl_vm_address_t methodListCursor = methodListPtr;
    
    while (true) {
        /* Grab a method list pointer. How to do that depends on whether
         * CLS_NO_METHOD_ARRAY is set. Once done, thisListPtr contains
         * a pointer to the method_list structure to read. */
        pl_vm_address_t thisListPtr;
        if (hasMultipleMethodLists) {
            /* If there are multiple method lists, then read the list pointer
             * from the current cursor, and advance the cursor. */
            uint32_t ptr;
            err = plcrash_async_task_memcpy(image->task, methodListCursor, 0, &ptr, sizeof(ptr));
            if (err != PLCRASH_ESUCCESS) {
                PLCF_DEBUG("plcrash_async_task_memcpy at 0x%llx error %d", (long long)methodListCursor, err);
                goto cleanup;
            }
            
            thisListPtr = image->byteorder->swap32(ptr);
            /* The end of the list is indicated with NULL or
             * END_OF_METHODS_LIST (the ObjC runtime source checks both). */
            if (thisListPtr == 0 || thisListPtr == END_OF_METHODS_LIST)
                break;
            
            methodListCursor += sizeof(ptr);
        } else {
            /* If CLS_NO_METHOD_ARRAY is set, then the single method_list
             * is pointed to by the cursor. */
            thisListPtr = methodListCursor;
            
            /* The pointer may be NULL, in which case there are no methods. */
            if (thisListPtr == 0)
                break;
        }
        
        /* Read a method_list structure from the current list pointer. */
        struct pl_objc1_method_list methodList;
        err = plcrash_async_task_memcpy(image->task, thisListPtr, 0, &methodList, sizeof(methodList));
        if (err != PLCRASH_ESUCCESS) {
            PLCF_DEBUG("plcrash_async_task_memcpy at 0x%llx error %d", (long long)methodListPtr, err);
            goto cleanup;
        }
        
        /* Find out how many methods are in the list, and iterate. */
        uint32_t count = image->byteorder->swap32(methodList.count);
        for (uint32_t i = 0; i < count; i++) {
            /* Method structures are laid out directly following the
             * method_list structure. */
            struct pl_objc1_method method;
            pl_vm_address_t methodPtr = thisListPtr + sizeof(methodList) + i * sizeof(method);
            err = plcrash_async_task_memcpy(image->task, methodPtr, 0, &method, sizeof(method));
            if (err != PLCRASH_ESUCCESS) {
                PLCF_DEBUG("plcrash_async_task_memcpy at 0x%llx error %d", (long long)methodPtr, err);
                goto cleanup;
            }
            
            /* Load the method name from the .name field pointer. */
            pl_vm_address_t methodNamePtr = image->byteorder->swap32(method.name);
            plcrash_async_macho_string_t methodName;
            err = plcrash_async_macho_string_init(&methodName, image, methodNamePtr);
            if (err != PLCRASH_ESUCCESS) {
                PLCF_DEBUG("plcrash_async_macho_string_init at 0x%llx error %d", (long long)methodNamePtr, err);
                goto cleanup;
            }
            
            /* Grab the method's IMP as well. */
            pl_vm_address_t imp = image->byteorder->swap32(method.imp);
            
            /* Callback! */
            callback(isMetaClass, &className, &methodName, imp, ctx);
            
            /* Clean up the method name object. */
            plcrash_async_macho_string_free(&methodName);
        }
        
        /* Bail out of the loop after a single iteration if
         * CLS_NO_METHOD_ARRAY is set, because there's no need
         * to iterate in that case. */
        if (!hasMultipleMethodLists)
            break;
    }
    
cleanup:
    if (classNameInitialized)
        plcrash_async_macho_string_free(&className);
    
    return err;
}