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;
}