/** * Initialize the DWARF opcode stream. * * @param mobj The memory object from which the expression opcodes will be read. This object must * remain valid for the lifetime of the opstream instance. * @param byteorder The byte order of the data referenced by @a mobj and @a thread_state. * @param address The task-relative address within @a mobj at which the opcodes will be fetched. * @param offset An offset to be applied to @a address. * @param length The total length of the opcodes readable at @a address + @a offset. */ plcrash_error_t dwarf_opstream::init (plcrash_async_mobject_t *mobj, const plcrash_async_byteorder_t *byteorder, pl_vm_address_t address, pl_vm_off_t offset, pl_vm_size_t length) { _mobj = mobj; _byteorder = byteorder; /* Calculate the start and end addresses */ if (!plcrash_async_address_apply_offset(address, offset, &_start)) { PLCF_DEBUG("Offset overflows base address"); return PLCRASH_EINVAL; } if (length > PL_VM_OFF_MAX || !plcrash_async_address_apply_offset(_start, length, &_end)) { PLCF_DEBUG("Length overflows base address"); return PLCRASH_EINVAL; } /* Map in the full instruction range */ _instr = plcrash_async_mobject_remap_address(mobj, _start, 0, _end-_start); _instr_max = (uint8_t *)_instr + (_end - _start); _p = _instr; if (_instr == NULL) { PLCF_DEBUG("Could not map the DWARF instructions; range falls outside mapped pages"); return PLCRASH_EINVAL; } return PLCRASH_ESUCCESS; }
/** * Read a single byte from @a mobj. * * @param mobj Memory object from which to read the value. * @param address The base address to be read. This address should be relative to the target task's address space. * @param offset An offset to be applied to @a address. * @param result The destination to which the data will be written. * * @return Returns PLCRASH_ESUCCESS on success, PLCRASH_EINVAL if the target address does not fall within the @a mobj address * range, or one of the plcrash_error_t constants for other error conditions. */ plcrash_error_t plcrash_async_mobject_read_uint8 (plcrash_async_mobject_t *mobj, pl_vm_address_t address, pl_vm_off_t offset, uint8_t *result) { uint8_t *input = plcrash_async_mobject_remap_address(mobj, address, offset, sizeof(uint8_t)); if (input == NULL) return PLCRASH_EINVAL; *result = *input; return PLCRASH_ESUCCESS; }
/** * Read a 64-bit value from @a mobj. * * @param mobj Memory object from which to read the value. * @param byteorder Byte order of the target value. * @param address The base address to be read. This address should be relative to the target task's address space. * @param offset An offset to be applied to @a address. * @param result The destination to which the data will be written, after @a byteorder has been applied. * * @return Returns PLCRASH_ESUCCESS on success, PLCRASH_EINVAL if the target address does not fall within the @a mobj address * range, or one of the plcrash_error_t constants for other error conditions. */ plcrash_error_t plcrash_async_mobject_read_uint64 (plcrash_async_mobject_t *mobj, const plcrash_async_byteorder_t *byteorder, pl_vm_address_t address, pl_vm_off_t offset, uint64_t *result) { uint64_t *input = plcrash_async_mobject_remap_address(mobj, address, offset, sizeof(uint64_t)); if (input == NULL) return PLCRASH_EINVAL; *result = byteorder->swap64(*input); return PLCRASH_ESUCCESS; }
PLCR_CPP_BEGIN_ASYNC_NS /** * @internal * @ingroup plcrash_async_dwarf_private * @defgroup plcrash_async_dwarf_private_opstream Generic DWARF Opcode Stream * @{ */ /** * Initialize the DWARF opcode stream. * * @param mobj The memory object from which the expression opcodes will be read. This object must * remain valid for the lifetime of the opstream instance. * @param byteorder The byte order of the data referenced by @a mobj and @a thread_state. * @param address The task-relative address within @a mobj at which the opcodes will be fetched. * @param offset An offset to be applied to @a address. * @param length The total length of the opcodes readable at @a address + @a offset. */ plcrash_error_t dwarf_opstream::init (plcrash_async_mobject_t *mobj, const plcrash_async_byteorder_t *byteorder, pl_vm_address_t address, pl_vm_off_t offset, pl_vm_size_t length) { _mobj = mobj; _byteorder = byteorder; /* Calculate the start and end addresses */ if (!plcrash_async_address_apply_offset(address, offset, &_start)) { PLCF_DEBUG("Offset overflows base address"); return PLCRASH_EINVAL; } if (length > PL_VM_OFF_MAX || !plcrash_async_address_apply_offset(_start, length, &_end)) { PLCF_DEBUG("Length overflows base address"); return PLCRASH_EINVAL; } /* Map in the full instruction range */ _instr = plcrash_async_mobject_remap_address(mobj, _start, 0, _end-_start); _instr_max = (uint8_t *)_instr + (_end - _start); _p = _instr; if (_instr == NULL) { PLCF_DEBUG("Could not map the DWARF instructions; range falls outside mapped pages"); return PLCRASH_EINVAL; } return PLCRASH_ESUCCESS; }
/** * Iterate over the available Mach-O LC_CMD entries. * * @param image The image to iterate * @param previous The previously returned LC_CMD address value, or 0 to iterate from the first LC_CMD. * @return Returns the address of the next load_command on success, or NULL on failure. * * @note A returned command is gauranteed to be readable, and fully within mapped address space. If the command * command can not be verified to have available MAX(sizeof(struct load_command), cmd->cmdsize) bytes, NULL will be * returned. */ void *plcrash_async_macho_next_command (plcrash_async_macho_t *image, void *previous) { struct load_command *cmd; /* On the first iteration, determine the LC_CMD offset from the Mach-O header. */ if (previous == NULL) { /* Sanity check */ if (image->byteorder->swap32(image->header.sizeofcmds) < sizeof(struct load_command)) { PLCF_DEBUG("Mach-O sizeofcmds is less than sizeof(struct load_command) in %s", image->name); return NULL; } return plcrash_async_mobject_remap_address(&image->load_cmds, image->header_addr, image->header_size, sizeof(struct load_command)); } /* We need the size from the previous load command; first, verify the pointer. */ cmd = previous; if (!plcrash_async_mobject_verify_local_pointer(&image->load_cmds, (uintptr_t) cmd, 0, sizeof(*cmd))) { PLCF_DEBUG("Failed to map LC_CMD at address %p in: %s", cmd, image->name); return NULL; } /* Advance to the next command */ uint32_t cmdsize = image->byteorder->swap32(cmd->cmdsize); void *next = ((uint8_t *)previous) + cmdsize; /* Avoid walking off the end of the cmd buffer */ if ((uintptr_t)next >= image->load_cmds.address + image->load_cmds.length) return NULL; /* Verify that it holds at least load_command */ if (!plcrash_async_mobject_verify_local_pointer(&image->load_cmds, (uintptr_t) next, 0, sizeof(struct load_command))) { PLCF_DEBUG("Failed to map LC_CMD at address %p in: %s", cmd, image->name); return NULL; } /* Verify the actual size. */ cmd = next; if (!plcrash_async_mobject_verify_local_pointer(&image->load_cmds, (uintptr_t) next, 0, image->byteorder->swap32(cmd->cmdsize))) { PLCF_DEBUG("Failed to map LC_CMD at address %p in: %s", cmd, image->name); return NULL; } return next; }
/** * Initialize a new symbol table reader, mapping the LINKEDIT segment from @a image into the current process. * * @param reader The reader to be initialized. * @param image The image from which the symbol table will be mapped. * * @return On success, returns PLCRASH_ESUCCESS. On failure, one of the plcrash_error_t error values will be returned, and no * mapping will be performed. */ plcrash_error_t plcrash_async_macho_symtab_reader_init (plcrash_async_macho_symtab_reader_t *reader, plcrash_async_macho_t *image) { plcrash_error_t retval; /* Fetch the symtab commands, if available. */ struct symtab_command *symtab_cmd = plcrash_async_macho_find_command(image, LC_SYMTAB); struct dysymtab_command *dysymtab_cmd = plcrash_async_macho_find_command(image, LC_DYSYMTAB); /* The symtab command is required */ if (symtab_cmd == NULL) { PLCF_DEBUG("could not find LC_SYMTAB load command"); return PLCRASH_ENOTFOUND; } /* Map in the __LINKEDIT segment, which includes the symbol and string tables */ plcrash_error_t err = plcrash_async_macho_map_segment(image, "__LINKEDIT", &reader->linkedit); if (err != PLCRASH_ESUCCESS) { PLCF_DEBUG("plcrash_async_mobject_init() failure: %d in %s", err, image->name); return PLCRASH_EINTERNAL; } /* Determine the string and symbol table sizes. */ uint32_t nsyms = image->byteorder->swap32(symtab_cmd->nsyms); size_t nlist_struct_size = image->m64 ? sizeof(struct nlist_64) : sizeof(struct nlist); size_t nlist_table_size = nsyms * nlist_struct_size; size_t string_size = image->byteorder->swap32(symtab_cmd->strsize); /* Fetch pointers to the symbol and string tables, and verify their size values */ void *nlist_table; char *string_table; nlist_table = plcrash_async_mobject_remap_address(&reader->linkedit.mobj, reader->linkedit.mobj.task_address, (image->byteorder->swap32(symtab_cmd->symoff) - reader->linkedit.fileoff), nlist_table_size); if (nlist_table == NULL) { PLCF_DEBUG("plcrash_async_mobject_remap_address(mobj, %" PRIx64 ", %" PRIx64") returned NULL mapping __LINKEDIT.symoff in %s", (uint64_t) reader->linkedit.mobj.address + image->byteorder->swap32(symtab_cmd->symoff), (uint64_t) nlist_table_size, image->name); retval = PLCRASH_EINTERNAL; goto cleanup; } string_table = plcrash_async_mobject_remap_address(&reader->linkedit.mobj, reader->linkedit.mobj.task_address, (image->byteorder->swap32(symtab_cmd->stroff) - reader->linkedit.fileoff), string_size); if (string_table == NULL) { PLCF_DEBUG("plcrash_async_mobject_remap_address(mobj, %" PRIx64 ", %" PRIx64") returned NULL mapping __LINKEDIT.stroff in %s", (uint64_t) reader->linkedit.mobj.address + image->byteorder->swap32(symtab_cmd->stroff), (uint64_t) string_size, image->name); retval = PLCRASH_EINTERNAL; goto cleanup; } /* Initialize common elements. */ reader->image = image; reader->string_table = string_table; reader->string_table_size = string_size; reader->symtab = nlist_table; reader->nsyms = nsyms; /* Initialize the local/global table pointers, if available */ if (dysymtab_cmd != NULL) { /* dysymtab is available; use it to constrain our symbol search to the global and local sections of the symbol table. */ uint32_t idx_syms_global = image->byteorder->swap32(dysymtab_cmd->iextdefsym); uint32_t idx_syms_local = image->byteorder->swap32(dysymtab_cmd->ilocalsym); uint32_t nsyms_global = image->byteorder->swap32(dysymtab_cmd->nextdefsym); uint32_t nsyms_local = image->byteorder->swap32(dysymtab_cmd->nlocalsym); /* Sanity check the symbol offsets to ensure they're within our known-valid ranges */ if (idx_syms_global + nsyms_global > nsyms || idx_syms_local + nsyms_local > nsyms) { PLCF_DEBUG("iextdefsym=%" PRIx32 ", ilocalsym=%" PRIx32 " out of range nsym=%" PRIx32, idx_syms_global+nsyms_global, idx_syms_local+nsyms_local, nsyms); retval = PLCRASH_EINVAL; goto cleanup; } /* Initialize reader state */ reader->nsyms_global = nsyms_global; reader->nsyms_local = nsyms_local; if (image->m64) { struct nlist_64 *n64 = nlist_table; reader->symtab_global = (pl_nlist_common *) (n64 + idx_syms_global); reader->symtab_local = (pl_nlist_common *) (n64 + idx_syms_local); } else { struct nlist *n32 = nlist_table; reader->symtab_global = (pl_nlist_common *) (n32 + idx_syms_global); reader->symtab_local = (pl_nlist_common *) (n32 + idx_syms_local); } } return PLCRASH_ESUCCESS; cleanup: plcrash_async_macho_mapped_segment_free(&reader->linkedit); return retval; }