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); }