Beispiel #1
0
static load_return_t
load_dylinker(
	struct dylinker_command	*lcp,
	integer_t		archbits,
	int			depth,
	int64_t			slide,
	load_result_t		*result
)
{
	char			*name;
	char			*p;
	uint8_t		*vp = NULL;	/* set by get_macho_vnode() */
	struct mach_header	*header;
	off_t			file_offset = 0; /* set by get_macho_vnode() */
	off_t			macho_size = 0;	/* set by get_macho_vnode() */
	load_result_t		*myresult;
	kern_return_t		ret;
	struct macho_data	*macho_data;
	struct {
		struct mach_header	__header;
		load_result_t		__myresult;
		struct macho_data	__macho_data;
	} *dyld_data;

	if (lcp->cmdsize < sizeof(*lcp))
		return (LOAD_BADMACHO);

	name = (char *)lcp + lcp->name.offset;
	/*
	 *	Check for a proper null terminated string.
	 */
	p = name;
	do {
		if (p >= (char *)lcp + lcp->cmdsize)
			return(LOAD_BADMACHO);
	} while (*p++);

	/* Allocate wad-of-data from heap to reduce excessively deep stacks */

    printf("load_dylinker 1: %s\n", name);
    
    dyld_data = malloc(sizeof (*dyld_data));
	header = &dyld_data->__header;
	myresult = &dyld_data->__myresult;
	macho_data = &dyld_data->__macho_data;

	ret = get_macho_vnode(name, archbits, header,
	    &file_offset, &macho_size, macho_data, &vp);
	if (ret)
		goto novp_out;

	*myresult = load_result_null;

	/*
	 *	First try to map dyld in directly.  This should work most of
	 *	the time since there shouldn't normally be something already
	 *	mapped to its address.
	 */

    vp = vp + file_offset;
    file_offset = 0;
    header = (struct mach_header*)vp;
    
	ret = parse_machfile(vp, header, file_offset, macho_size, depth, slide, 0, myresult);

	/*
	 *	If it turned out something was in the way, then we'll take
	 *	take this longer path to preflight dyld's vm ranges, then
	 *	map it at a free location in the address space.
	 */

	if (ret == LOAD_NOSPACE) {
		mach_vm_offset_t	dyl_start, map_addr;
		mach_vm_size_t	dyl_length;
		int64_t			slide_amount;

		*myresult = load_result_null;

		/*
		 * Preflight parsing the Mach-O file with a NULL
		 * map, which will return the ranges needed for a
		 * subsequent map attempt (with a slide) in "myresult"
		 */
		ret = parse_machfile(vp, header,
		                     file_offset, macho_size, depth,
		                     0 /* slide */, 0, myresult);

		if (ret != LOAD_SUCCESS) {
			goto novp_out;
		}

		dyl_start = myresult->min_vm_addr;
		dyl_length = myresult->max_vm_addr - myresult->min_vm_addr;

		dyl_length += slide;

		/* To find an appropriate load address, do a quick allocation */
		map_addr = dyl_start;
        map_addr = (mach_vm_offset_t)malloc(dyl_length);
		if (ret == 0) {
			ret = LOAD_NOSPACE;
			goto novp_out;
		}

        free((void*)map_addr);
		
		if (map_addr < dyl_start)
			slide_amount = -(int64_t)(dyl_start - map_addr);
		else
			slide_amount = (int64_t)(map_addr - dyl_start);

		slide_amount += slide;

		*myresult = load_result_null;

		ret = parse_machfile(vp, header,
		                     file_offset, macho_size, depth,
		                     slide_amount, 0, myresult);

		if (ret) {
			goto novp_out;
		}
	}

	if (ret == LOAD_SUCCESS) {		
		result->dynlinker = TRUE;
		result->entry_point = myresult->entry_point;
		result->validentry = myresult->validentry;
		result->all_image_info_addr = myresult->all_image_info_addr;
		result->all_image_info_size = myresult->all_image_info_size;
	}
    
novp_out:
	free(dyld_data);
	return (ret);

}
static
load_return_t
load_dylinker(
    struct dylinker_command	*lcp,
    integer_t		archbits,
    vm_map_t		map,
    thread_t	thread,
    int			depth,
    load_result_t		*result,
    boolean_t		is_64bit
)
{
    char			*name;
    char			*p;
    struct vnode		*vp = NULLVP;	/* set by get_macho_vnode() */
    struct mach_header	header;
    off_t			file_offset = 0; /* set by get_macho_vnode() */
    off_t			macho_size = 0;	/* set by get_macho_vnode() */
    vm_map_t		copy_map;
    load_result_t		myresult;
    kern_return_t		ret;
    vm_map_copy_t	tmp;
    mach_vm_offset_t	dyl_start, map_addr;
    mach_vm_size_t		dyl_length;

    name = (char *)lcp + lcp->name.offset;
    /*
     *	Check for a proper null terminated string.
     */
    p = name;
    do {
        if (p >= (char *)lcp + lcp->cmdsize)
            return(LOAD_BADMACHO);
    } while (*p++);

    ret = get_macho_vnode(name, archbits, &header, &file_offset, &macho_size, &vp);
    if (ret)
        return (ret);

    myresult = load_result_null;

    /*
     *	First try to map dyld in directly.  This should work most of
     *	the time since there shouldn't normally be something already
     *	mapped to its address.
     */

    ret = parse_machfile(vp, map, thread, &header, file_offset, macho_size,
                         depth, &myresult);

    /*
     *	If it turned out something was in the way, then we'll take
     *	take this longer path to map dyld into a temporary map and
     *	copy it into destination map at a different address.
     */

    if (ret == LOAD_NOSPACE) {

        /*
         *	Load the Mach-O.
         *	Use a temporary map to do the work.
         */
        copy_map = vm_map_create(pmap_create(vm_map_round_page(macho_size),
                                             is_64bit),
                                 get_map_min(map), get_map_max(map), TRUE);
        if (VM_MAP_NULL == copy_map) {
            ret = LOAD_RESOURCE;
            goto out;
        }

        myresult = load_result_null;

        ret = parse_machfile(vp, copy_map, thread, &header,
                             file_offset, macho_size,
                             depth, &myresult);

        if (ret) {
            vm_map_deallocate(copy_map);
            goto out;
        }

        if (get_map_nentries(copy_map) > 0) {

            dyl_start = mach_get_vm_start(copy_map);
            dyl_length = mach_get_vm_end(copy_map) - dyl_start;

            map_addr = dyl_start;
            ret = mach_vm_allocate(map, &map_addr, dyl_length, VM_FLAGS_ANYWHERE);

            if (ret != KERN_SUCCESS) {
                vm_map_deallocate(copy_map);
                ret = LOAD_NOSPACE;
                goto out;

            }

            ret = vm_map_copyin(copy_map,
                                (vm_map_address_t)dyl_start,
                                (vm_map_size_t)dyl_length,
                                TRUE, &tmp);
            if (ret != KERN_SUCCESS) {
                (void) vm_map_remove(map,
                                     vm_map_trunc_page(map_addr),
                                     vm_map_round_page(map_addr + dyl_length),
                                     VM_MAP_NO_FLAGS);
                vm_map_deallocate(copy_map);
                goto out;
            }

            ret = vm_map_copy_overwrite(map,
                                        (vm_map_address_t)map_addr,
                                        tmp, FALSE);
            if (ret != KERN_SUCCESS) {
                vm_map_copy_discard(tmp);
                (void) vm_map_remove(map,
                                     vm_map_trunc_page(map_addr),
                                     vm_map_round_page(map_addr + dyl_length),
                                     VM_MAP_NO_FLAGS);
                vm_map_deallocate(copy_map);
                goto out;
            }

            if (map_addr != dyl_start)
                myresult.entry_point += (map_addr - dyl_start);
        } else {
            ret = LOAD_FAILURE;
        }

        vm_map_deallocate(copy_map);
    }

    if (ret == LOAD_SUCCESS) {
        result->dynlinker = TRUE;
        result->entry_point = myresult.entry_point;
        (void)ubc_map(vp, PROT_READ | PROT_EXEC);
    }
out:
    vnode_put(vp);
    return (ret);

}