in Source/PLCrashAsyncMachOImage.c [66:211]
plcrash_error_t plcrash_nasync_macho_init (plcrash_async_macho_t *image, mach_port_t task, const char *name, pl_vm_address_t header) {
plcrash_error_t ret;
/* Defaults checked in the error cleanup handler */
bool mobj_initialized = false;
bool task_initialized = false;
image->name = NULL;
/* Basic initialization */
image->task = task;
image->header_addr = header;
image->name = strdup(name);
mach_port_mod_refs(mach_task_self(), image->task, MACH_PORT_RIGHT_SEND, 1);
task_initialized = true;
/* Read in the Mach-O header */
if ((ret = plcrash_async_task_memcpy(image->task, image->header_addr, 0, &image->header, sizeof(image->header))) != PLCRASH_ESUCCESS) {
/* NOTE: The image struct must be fully initialized before returning here, as otherwise our _free() function
* will crash */
PLCF_DEBUG("Failed to read Mach-O header from 0x%" PRIx64 " for image %s, ret=%d", (uint64_t) image->header_addr, name, ret);
ret = PLCRASH_EINTERNAL;
goto error;
}
/* Set the default byte order*/
image->byteorder = &plcrash_async_byteorder_direct;
/* Parse the Mach-O magic identifier. */
switch (image->header.magic) {
case MH_CIGAM:
// Enable byte swapping
image->byteorder = &plcrash_async_byteorder_swapped;
// Fall-through
case MH_MAGIC:
image->m64 = false;
break;
case MH_CIGAM_64:
// Enable byte swapping
image->byteorder = &plcrash_async_byteorder_swapped;
// Fall-through
case MH_MAGIC_64:
image->m64 = true;
break;
case FAT_CIGAM:
case FAT_MAGIC:
PLCF_DEBUG("%s called with an unsupported universal Mach-O archive in: %s", __func__, PLCF_DEBUG_IMAGE_NAME(image));
return PLCRASH_EINVAL;
break;
default:
PLCF_DEBUG("Unknown Mach-O magic: 0x%" PRIx32 " in: %s", image->header.magic, PLCF_DEBUG_IMAGE_NAME(image));
return PLCRASH_EINVAL;
}
/* Save the header size */
if (image->m64) {
image->header_size = sizeof(struct mach_header_64);
} else {
image->header_size = sizeof(struct mach_header);
}
/* Map in header + load commands */
pl_vm_size_t cmd_len = image->byteorder->swap32(image->header.sizeofcmds);
pl_vm_size_t cmd_offset = image->header_addr + image->header_size;
image->ncmds = image->byteorder->swap32(image->header.ncmds);
ret = plcrash_async_mobject_init(&image->load_cmds, image->task, cmd_offset, cmd_len, true);
if (ret != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Failed to map Mach-O load commands in image %s", PLCF_DEBUG_IMAGE_NAME(image));
goto error;
} else {
mobj_initialized = true;
}
/* Now that the image has been sufficiently initialized, determine the __TEXT segment size */
void *cmdptr = NULL;
image->text_size = 0x0;
bool found_text_seg = false;
while ((cmdptr = plcrash_async_macho_next_command_type(image, cmdptr, image->m64 ? LC_SEGMENT_64 : LC_SEGMENT)) != 0) {
if (image->m64) {
struct segment_command_64 *segment = cmdptr;
if (!plcrash_async_mobject_verify_local_pointer(&image->load_cmds, (uintptr_t) segment, 0, sizeof(*segment))) {
PLCF_DEBUG("LC_SEGMENT command was too short");
ret = PLCRASH_EINVAL;
goto error;
}
if (plcrash_async_strncmp(segment->segname, SEG_TEXT, sizeof(segment->segname)) != 0)
continue;
image->text_size = (pl_vm_size_t) image->byteorder->swap64(segment->vmsize);
image->text_vmaddr = (pl_vm_address_t) image->byteorder->swap64(segment->vmaddr);
found_text_seg = true;
break;
} else {
struct segment_command *segment = cmdptr;
if (!plcrash_async_mobject_verify_local_pointer(&image->load_cmds, (uintptr_t) segment, 0, sizeof(*segment))) {
PLCF_DEBUG("LC_SEGMENT command was too short");
ret = PLCRASH_EINVAL;
goto error;
}
if (plcrash_async_strncmp(segment->segname, SEG_TEXT, sizeof(segment->segname)) != 0)
continue;
image->text_size = image->byteorder->swap32(segment->vmsize);
image->text_vmaddr = image->byteorder->swap32(segment->vmaddr);
found_text_seg = true;
break;
}
}
if (!found_text_seg) {
PLCF_DEBUG("Could not find __TEXT segment!");
ret = PLCRASH_EINVAL;
goto error;
}
/* Compute the vmaddr slide */
if (image->text_vmaddr < header) {
image->vmaddr_slide = header - image->text_vmaddr;
} else if (image->text_vmaddr > header) {
image->vmaddr_slide = -((pl_vm_off_t) (image->text_vmaddr - header));
} else {
image->vmaddr_slide = 0;
}
return PLCRASH_ESUCCESS;
error:
if (mobj_initialized)
plcrash_async_mobject_free(&image->load_cmds);
if (image->name != NULL)
free(image->name);
if (task_initialized)
mach_port_mod_refs(mach_task_self(), image->task, MACH_PORT_RIGHT_SEND, -1);
return ret;
}