Exemplo n.º 1
0
/**
 * 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;
}