// PLFrameWalker API plframe_error_t plframe_cursor_next (plframe_cursor_t *cursor) { kern_return_t kr; void *prevfp = cursor->fp[0]; /* Fetch the next stack address */ if (cursor->init_frame) { /* The first frame is already available, so there's nothing to do */ cursor->init_frame = false; return PLFRAME_ESUCCESS; } else { if (cursor->fp[0] == NULL) { /* No frame data has been loaded, fetch it from register state */ kr = plcrash_async_read_addr(mach_task_self(), cursor->uap->uc_mcontext->__ss.__rbp, cursor->fp, sizeof(cursor->fp)); } else { /* Frame data loaded, walk the stack */ kr = plcrash_async_read_addr(mach_task_self(), (pl_vm_address_t) cursor->fp[0], cursor->fp, sizeof(cursor->fp)); } } /* Was the read successful? */ if (kr != KERN_SUCCESS) return PLFRAME_EBADFRAME; /* Check for completion */ if (cursor->fp[0] == NULL) return PLFRAME_ENOFRAME; /* Is the stack growing in the right direction? */ if (!cursor->init_frame && prevfp > cursor->fp[0]) return PLFRAME_EBADFRAME; /* New frame fetched */ return PLFRAME_ESUCCESS; }
/** * Initialize a new Mach-O binary image parser. * * @param image The image structure to be initialized. * @param name The file name or path for the Mach-O image. * @param header The task-local address of the image's Mach-O header. * * @return PLCRASH_ESUCCESS on success. PLCRASH_EINVAL will be returned in the Mach-O file can not be parsed, * or PLCRASH_EINTERNAL if an error occurs reading from the target task. * * @warning This method is not async safe. */ 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 */ kern_return_t kt; if ((kt = plcrash_async_read_addr(image->task, image->header_addr, &image->header, sizeof(image->header))) != KERN_SUCCESS) { /* 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, kern_error=%d", (uint64_t) image->header_addr, name, kt); 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__, image->name); return PLCRASH_EINVAL; break; default: PLCF_DEBUG("Unknown Mach-O magic: 0x%" PRIx32 " in: %s", image->header.magic, image->name); 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", image->name); 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 = image->byteorder->swap64(segment->vmsize); image->text_vmaddr = 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; }