+#include <vm/vm_map.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_pager.h>
+#include <vm/vm_kern.h>
+#include <vm/task_working_set.h>
+#include <vm/vm_shared_memory_server.h>
+
+/*
+ * Mach things for which prototypes are unavailable from Mach headers
+ */
+void ipc_task_reset(
+ task_t task);
+
+extern struct savearea *get_user_regs(thread_t);
+
+
+#include <kern/thread.h>
+#include <kern/task.h>
+#include <kern/ast.h>
+#include <kern/mach_loader.h>
+#include <mach-o/fat.h>
+#include <mach-o/loader.h>
+#include <machine/vmparam.h>
+#if KTRACE
+#include <sys/ktrace.h>
+#endif
+#include <sys/imgact.h>
+
+
+/*
+ * SIZE_MAXPTR The maximum size of a user space pointer, in bytes
+ * SIZE_IMG_STRSPACE The available string space, minus two pointers; we
+ * define it interms of the maximum, since we don't
+ * know the pointer size going in, until after we've
+ * parsed the executable image.
+ */
+#define SIZE_MAXPTR 8 /* 64 bits */
+#define SIZE_IMG_STRSPACE (NCARGS - 2 * SIZE_MAXPTR)
+
+int app_profile = 0;
+
+extern vm_map_t bsd_pageable_map;
+extern struct fileops vnops;
+
+#define ROUND_PTR(type, addr) \
+ (type *)( ( (unsigned)(addr) + 16 - 1) \
+ & ~(16 - 1) )
+
+struct image_params; /* Forward */
+static int exec_copyout_strings(struct image_params *imgp, user_addr_t *stackp);
+static int load_return_to_errno(load_return_t lrtn);
+static int execargs_alloc(struct image_params *imgp);
+static int execargs_free(struct image_params *imgp);
+static int exec_check_permissions(struct image_params *imgp);
+static int exec_extract_strings(struct image_params *imgp);
+static int exec_handle_sugid(struct image_params *imgp);
+static int sugid_scripts = 0;
+SYSCTL_INT (_kern, OID_AUTO, sugid_scripts, CTLFLAG_RW, &sugid_scripts, 0, "");
+static kern_return_t create_unix_stack(vm_map_t map, user_addr_t user_stack,
+ int customstack, struct proc *p);
+static int copyoutptr(user_addr_t ua, user_addr_t ptr, int ptr_size);
+
+/* XXX forward; should be in headers, but can't be for one reason or another */
+extern void vfork_return(thread_t th_act,
+ struct proc * p,
+ struct proc *p2,
+ register_t *retval);
+
+/*
+ * exec_add_string
+ *
+ * Add the requested string to the string space area.
+ *
+ * Parameters; struct image_params * image parameter block
+ * user_addr_t string to add to strings area
+ * uio_seg segment where string is located
+ *
+ * Returns: 0 Success
+ * !0 Failure errno from copyinstr()
+ *
+ * Implicit returns:
+ * (imgp->ip_strendp) updated location of next add, if any
+ * (imgp->ip_strspace) updated byte count of space remaining
+ */
+static int
+exec_add_string(struct image_params *imgp, user_addr_t str, /*uio_seg*/int seg)
+{
+ int error = 0;
+
+ do {
+ size_t len = 0;
+ if (imgp->ip_strspace <= 0) {
+ error = E2BIG;
+ break;
+ }
+ if (IS_UIO_SYS_SPACE(seg)) {
+ char *kstr = CAST_DOWN(char *,str); /* SAFE */
+ error = copystr(kstr, imgp->ip_strendp, imgp->ip_strspace, &len);
+ } else {
+ error = copyinstr(str, imgp->ip_strendp, imgp->ip_strspace,
+ &len);
+ }
+ imgp->ip_strendp += len;
+ imgp->ip_strspace -= len;
+ } while (error == ENAMETOOLONG);
+
+ return error;
+}
+
+/*
+ * exec_save_path
+ *
+ * To support new app package launching for Mac OS X, the dyld needs the
+ * first argument to execve() stored on the user stack.
+ *
+ * Save the executable path name at the top of the strings area and set
+ * the argument vector pointer to the location following that to indicate
+ * the start of the argument and environment tuples, setting the remaining
+ * string space count to the size of the string area minus the path length
+ * and a reserve for two pointers.
+ *
+ * Parameters; struct image_params * image parameter block
+ * char * path used to invoke program
+ * uio_seg segment where path is located
+ *
+ * Returns: int 0 Success
+ * !0 Failure: error number
+ * Implicit returns:
+ * (imgp->ip_strings) saved path
+ * (imgp->ip_strspace) space remaining in ip_strings
+ * (imgp->ip_argv) beginning of argument list
+ * (imgp->ip_strendp) start of remaining copy area
+ *
+ * Note: We have to do this before the initial namei() since in the
+ * path contains symbolic links, namei() will overwrite the
+ * original path buffer contents. If the last symbolic link
+ * resolved was a relative pathname, we would lose the original
+ * "path", which could be an absolute pathname. This might be
+ * unacceptable for dyld.
+ */
+static int
+exec_save_path(struct image_params *imgp, user_addr_t path, /*uio_seg*/int seg)
+{
+ int error;
+ size_t len;
+ char *kpath = CAST_DOWN(char *,path); /* SAFE */
+
+ imgp->ip_strendp = imgp->ip_strings;
+ imgp->ip_strspace = SIZE_IMG_STRSPACE;
+
+ len = MIN(MAXPATHLEN, imgp->ip_strspace);
+
+ switch( seg) {
+ case UIO_USERSPACE32:
+ case UIO_USERSPACE64: /* Same for copyin()... */
+ error = copyinstr(path, imgp->ip_strings, len, &len);
+ break;
+ case UIO_SYSSPACE32:
+ error = copystr(kpath, imgp->ip_strings, len, &len);
+ break;
+ default:
+ error = EFAULT;
+ break;
+ }
+
+ if (!error) {
+ imgp->ip_strendp += len;
+ imgp->ip_strspace -= len;
+ imgp->ip_argv = imgp->ip_strendp;
+ }
+
+ return(error);
+}
+
+#ifdef IMGPF_POWERPC
+/*
+ * exec_powerpc32_imgact
+ *
+ * Implicitly invoke the PowerPC handler for a byte-swapped image magic
+ * number. This may happen either as a result of an attempt to invoke a
+ * PowerPC image directly, or indirectly as the interpreter used in an
+ * interpreter script.
+ *
+ * Parameters; struct image_params * image parameter block
+ *
+ * Returns: -1 not an PowerPC image (keep looking)
+ * -3 Success: exec_archhandler_ppc: relookup
+ * >0 Failure: exec_archhandler_ppc: error number
+ *
+ * Note: This image activator does not handle the case of a direct
+ * invocation of the exec_archhandler_ppc, since in that case, the
+ * exec_archhandler_ppc itself is not a PowerPC binary; instead,
+ * binary image activators must recognize the exec_archhandler_ppc;
+ * This is managed in exec_check_permissions().
+ *
+ * Note: This image activator is limited to 32 bit powerpc images;
+ * if support for 64 bit powerpc images is desired, it would
+ * be more in line with this design to write a separate 64 bit
+ * image activator.
+ */
+static int
+exec_powerpc32_imgact(struct image_params *imgp)
+{
+ struct mach_header *mach_header = (struct mach_header *)imgp->ip_vdata;
+ int error;
+ size_t len = 0;
+
+ /*
+ * Make sure it's a PowerPC binary. If we've already redirected
+ * from an interpreted file once, don't do it again.
+ */
+ if (mach_header->magic != MH_CIGAM)
+ return (-1);
+
+ /* If there is no exec_archhandler_ppc, we can't run it */
+ if (exec_archhandler_ppc.path[0] == 0)
+ return (EBADARCH);
+
+ /*
+ * The PowerPC flag will be set by the exec_check_permissions()
+ * call anyway; however, we set this flag here so that the relookup
+ * in execve() does not follow symbolic links, as a side effect.
+ */
+ imgp->ip_flags |= IMGPF_POWERPC;
+
+ /* impute an interpreter */
+ error = copystr(exec_archhandler_ppc.path, imgp->ip_interp_name,
+ IMG_SHSIZE, &len);
+ if (error)
+ return (error);
+
+ /*
+ * provide a replacement string for p->p_comm; we have to use an
+ * an alternate buffer for this, rather than replacing it directly,
+ * since the exec may fail and return to the parent. In that case,
+ * we would have erroneously changed the parent p->p_comm instead.
+ */
+ strncpy(imgp->ip_p_comm, imgp->ip_ndp->ni_cnd.cn_nameptr, MAXCOMLEN);
+ imgp->ip_p_comm[MAXCOMLEN] = '\0';
+
+ return (-3);
+}
+#endif /* IMGPF_POWERPC */
+
+
+/*
+ * exec_shell_imgact
+ *
+ * Image activator for interpreter scripts. If the image begins with the
+ * characters "#!", then it is an interpreter script. Verify that we are
+ * not already executing in PowerPC mode, and that the length of the script
+ * line indicating the interpreter is not in excess of the maximum allowed
+ * size. If this is the case, then break out the arguments, if any, which
+ * are separated by white space, and copy them into the argument save area
+ * as if they were provided on the command line before all other arguments.
+ * The line ends when we encounter a comment character ('#') or newline.
+ *
+ * Parameters; struct image_params * image parameter block
+ *
+ * Returns: -1 not an interpreter (keep looking)
+ * -3 Success: interpreter: relookup
+ * >0 Failure: interpreter: error number
+ *
+ * A return value other than -1 indicates subsequent image activators should
+ * not be given the opportunity to attempt to activate the image.
+ */
+static int
+exec_shell_imgact(struct image_params *imgp)
+{
+ char *vdata = imgp->ip_vdata;
+ char *ihp;
+ char *line_endp;
+ char *interp;
+
+ /*
+ * Make sure it's a shell script. If we've already redirected
+ * from an interpreted file once, don't do it again.
+ *
+ * Note: We disallow PowerPC, since the expectation is that we
+ * may run a PowerPC interpreter, but not an interpret a PowerPC
+ * image. This is consistent with historical behaviour.
+ */
+ if (vdata[0] != '#' ||
+ vdata[1] != '!' ||
+ (imgp->ip_flags & IMGPF_INTERPRET) != 0) {
+ return (-1);
+ }
+
+#ifdef IMGPF_POWERPC
+ if ((imgp->ip_flags & IMGPF_POWERPC) != 0)
+ return (EBADARCH);
+#endif /* IMGPF_POWERPC */
+
+ imgp->ip_flags |= IMGPF_INTERPRET;
+
+ /* Check to see if SUGID scripts are permitted. If they aren't then
+ * clear the SUGID bits.
+ * imgp->ip_vattr is known to be valid.
+ */
+ if (sugid_scripts == 0) {
+ imgp->ip_origvattr->va_mode &= ~(VSUID | VSGID);
+ }
+
+ /* Find the nominal end of the interpreter line */
+ for( ihp = &vdata[2]; *ihp != '\n' && *ihp != '#'; ihp++) {
+ if (ihp >= &vdata[IMG_SHSIZE])
+ return (ENOEXEC);
+ }
+
+ line_endp = ihp;
+ ihp = &vdata[2];
+ /* Skip over leading spaces - until the interpreter name */
+ while ( ihp < line_endp && ((*ihp == ' ') || (*ihp == '\t')))
+ ihp++;
+
+ /*
+ * Find the last non-whitespace character before the end of line or
+ * the beginning of a comment; this is our new end of line.
+ */
+ for (;line_endp > ihp && ((*line_endp == ' ') || (*line_endp == '\t')); line_endp--)
+ continue;
+
+ /* Empty? */
+ if (line_endp == ihp)
+ return (ENOEXEC);
+
+ /* copy the interpreter name */
+ interp = imgp->ip_interp_name;
+ while ((ihp < line_endp) && (*ihp != ' ') && (*ihp != '\t'))
+ *interp++ = *ihp++;
+ *interp = '\0';
+
+ exec_save_path(imgp, CAST_USER_ADDR_T(imgp->ip_interp_name),
+ UIO_SYSSPACE32);
+
+ ihp = &vdata[2];
+ while (ihp < line_endp) {
+ /* Skip leading whitespace before each argument */
+ while ((*ihp == ' ') || (*ihp == '\t'))
+ ihp++;
+
+ if (ihp >= line_endp)
+ break;
+
+ /* We have an argument; copy it */
+ while ((ihp < line_endp) && (*ihp != ' ') && (*ihp != '\t')) {
+ *imgp->ip_strendp++ = *ihp++;
+ imgp->ip_strspace--;
+ }
+ *imgp->ip_strendp++ = 0;
+ imgp->ip_strspace--;
+ imgp->ip_argc++;
+ }
+
+ return (-3);
+}
+
+
+
+/*
+ * exec_fat_imgact
+ *
+ * Image activator for fat 1.0 binaries. If the binary is fat, then we
+ * need to select an image from it internally, and make that the image
+ * we are going to attempt to execute. At present, this consists of
+ * reloading the first page for the image with a first page from the
+ * offset location indicated by the fat header.
+ *
+ * Important: This image activator is byte order neutral.
+ *
+ * Note: If we find an encapsulated binary, we make no assertions
+ * about its validity; instead, we leave that up to a rescan
+ * for an activator to claim it, and, if it is claimed by one,
+ * that activator is responsible for determining validity.
+ */
+static int
+exec_fat_imgact(struct image_params *imgp)
+{
+ struct proc *p = vfs_context_proc(imgp->ip_vfs_context);
+ kauth_cred_t cred = p->p_ucred;
+ struct fat_header *fat_header = (struct fat_header *)imgp->ip_vdata;
+ struct fat_arch fat_arch;
+ int resid, error;
+ load_return_t lret;
+
+ /* Make sure it's a fat binary */
+ if ((fat_header->magic != FAT_MAGIC) &&
+ (fat_header->magic != FAT_CIGAM)) {
+ error = -1;
+ goto bad;
+ }
+
+ /* Look up our preferred architecture in the fat file. */
+ lret = fatfile_getarch_affinity(imgp->ip_vp,
+ (vm_offset_t)fat_header,
+ &fat_arch,
+ (p->p_flag & P_AFFINITY));
+ if (lret != LOAD_SUCCESS) {
+ error = load_return_to_errno(lret);
+ goto bad;
+ }
+
+ /* Read the Mach-O header out of it */
+ error = vn_rdwr(UIO_READ, imgp->ip_vp, imgp->ip_vdata,
+ PAGE_SIZE, fat_arch.offset,
+ UIO_SYSSPACE32, (IO_UNIT|IO_NODELOCKED),
+ cred, &resid, p);
+ if (error) {
+ goto bad;
+ }
+
+ /* Did we read a complete header? */
+ if (resid) {
+ error = EBADEXEC;
+ goto bad;
+ }
+
+ /* Success. Indicate we have identified an encapsulated binary */
+ error = -2;
+ imgp->ip_arch_offset = (user_size_t)fat_arch.offset;
+ imgp->ip_arch_size = (user_size_t)fat_arch.size;
+
+bad:
+ return (error);
+}
+
+/*
+ * exec_mach_imgact
+ *
+ * Image activator for mach-o 1.0 binaries.
+ *
+ * Important: This image activator is NOT byte order neutral.
+ */
+static int
+exec_mach_imgact(struct image_params *imgp)
+{
+ struct mach_header *mach_header = (struct mach_header *)imgp->ip_vdata;
+ kauth_cred_t cred = vfs_context_ucred(imgp->ip_vfs_context);
+ struct proc *p = vfs_context_proc(imgp->ip_vfs_context);
+ int error = 0;
+ int vfexec = 0;
+ task_t task;
+ task_t new_task;
+ thread_t thread;
+ struct uthread *uthread;
+ vm_map_t old_map = VM_MAP_NULL;
+ vm_map_t map;
+ boolean_t clean_regions = FALSE;
+ load_return_t lret;
+ load_result_t load_result;
+ shared_region_mapping_t shared_region, initial_region;
+#ifdef IMGPF_POWERPC
+ int powerpcParent, powerpcImage;
+#endif /* IMGPF_POWERPC */
+
+ /*
+ * make sure it's a Mach-O 1.0 or Mach-O 2.0 binary; the difference
+ * is a reserved field on the end, so for the most part, we can
+ * treat them as if they were identical.
+ */
+ if ((mach_header->magic != MH_MAGIC) &&
+ (mach_header->magic != MH_MAGIC_64)) {
+ error = -1;
+ goto bad;
+ }
+
+ task = current_task();
+ thread = current_thread();
+ uthread = get_bsdthread_info(thread);
+
+ if (uthread->uu_flag & UT_VFORK)
+ vfexec = 1; /* Mark in exec */
+
+ if ((mach_header->cputype & CPU_ARCH_ABI64) == CPU_ARCH_ABI64)
+ imgp->ip_flags |= IMGPF_IS_64BIT;
+
+ if (!grade_binary(mach_header->cputype, mach_header->cpusubtype)) {
+ error = EBADARCH;
+ goto bad;
+ }
+
+ /*
+ * Copy in arguments/environment from the old process, if the
+ * vector is non-NULL (i.e. exec is not being called from
+ * load_init_program(), as a special case, at system startup).
+ */
+ if (imgp->ip_user_argv != 0LL) {
+ error = exec_extract_strings(imgp);
+ if (error)
+ goto bad;
+ }
+
+ /*
+ * Hack for binary compatability; put three NULs on the end of the
+ * string area, and round it up to the next word boundary. This
+ * ensures padding with NULs to the boundary.
+ */
+ imgp->ip_strendp[0] = 0;
+ imgp->ip_strendp[1] = 0;
+ imgp->ip_strendp[2] = 0;
+ imgp->ip_strendp += (((imgp->ip_strendp - imgp->ip_strings) + NBPW-1) & ~(NBPW-1));
+
+#ifdef IMGPF_POWERPC
+ /*
+ * XXX
+ *
+ * Should be factored out; this is here because we might be getting
+ * invoked this way as the result of a shell script, and the check
+ * in exec_check_permissions() is not interior to the jump back up
+ * to the "encapsulated_binary:" label in execve().
+ */
+ if (imgp->ip_vattr->va_fsid == exec_archhandler_ppc.fsid &&
+ imgp->ip_vattr->va_fileid == (uint64_t)((u_long)exec_archhandler_ppc.fileid)) {
+ imgp->ip_flags |= IMGPF_POWERPC;
+ }
+#endif /* IMGPF_POWERPC */
+
+ if (vfexec) {
+ kern_return_t result;
+
+ result = task_create_internal(task, FALSE, (imgp->ip_flags & IMGPF_IS_64BIT), &new_task);
+ if (result != KERN_SUCCESS)
+ printf("execve: task_create failed. Code: 0x%x\n", result);
+ p->task = new_task;
+ set_bsdtask_info(new_task, p);
+ if (p->p_nice != 0)
+ resetpriority(p);
+ map = get_task_map(new_task);
+
+ if (imgp->ip_flags & IMGPF_IS_64BIT)
+ vm_map_set_64bit(map);
+ else
+ vm_map_set_32bit(map);
+
+ result = thread_create(new_task, &imgp->ip_vfork_thread);
+ if (result != KERN_SUCCESS)
+ printf("execve: thread_create failed. Code: 0x%x\n", result);
+ /* reset local idea of task, thread, uthread */
+ task = new_task;
+ thread = imgp->ip_vfork_thread;
+ uthread = get_bsdthread_info(thread);
+ } else {
+ map = VM_MAP_NULL;
+ }
+
+ /*
+ * We set these flags here; this is OK, since if we fail after
+ * this point, we have already destroyed the parent process anyway.
+ */
+ if (imgp->ip_flags & IMGPF_IS_64BIT) {
+ task_set_64bit(task, TRUE);
+ p->p_flag |= P_LP64;
+ } else {
+ task_set_64bit(task, FALSE);
+ p->p_flag &= ~P_LP64;
+ }
+
+ /*
+ * Load the Mach-O file.
+ */
+/* LP64 - remove following "if" statement after osfmk/vm/task_working_set.c */
+if((imgp->ip_flags & IMGPF_IS_64BIT) == 0)
+ if(imgp->ip_tws_cache_name) {
+ tws_handle_startup_file(task, kauth_cred_getuid(cred),
+ imgp->ip_tws_cache_name, imgp->ip_vp, &clean_regions);
+ }
+
+ vm_get_shared_region(task, &initial_region);
+
+#ifdef IMGPF_POWERPC
+ /*
+ * If we are transitioning to/from powerpc, then we need to do extra
+ * work here.
+ */
+ powerpcParent = (p->p_flag & P_TRANSLATED) ? 1 : 0;
+ powerpcImage = (imgp->ip_flags & IMGPF_POWERPC) ? 1 : 0;
+
+ if (powerpcParent ^ powerpcImage) {
+ cpu_type_t cpu = (powerpcImage ? CPU_TYPE_POWERPC : cpu_type());
+ struct vnode *rootDir = p->p_fd->fd_rdir;
+
+ shared_region = lookup_default_shared_region((int)rootDir, cpu);
+ if (shared_region == NULL) {
+ shared_region_mapping_t old_region;
+ shared_region_mapping_t new_region;
+ vm_get_shared_region(current_task(), &old_region);
+ /* grrrr... this sets current_task(), not task
+ * -- they're different (usually)
+ */
+ shared_file_boot_time_init((int)rootDir,cpu);
+ if ( current_task() != task ) {
+ vm_get_shared_region(current_task(),&new_region);
+ vm_set_shared_region(task,new_region);
+ vm_set_shared_region(current_task(),old_region);
+ }
+ } else {
+ vm_set_shared_region(task, shared_region);
+ }
+ shared_region_mapping_dealloc(initial_region);
+ } else
+#endif /* IMGPF_POWERPC */
+
+ {
+ struct shared_region_task_mappings map_info;
+ shared_region_mapping_t next;
+
+ shared_region_mapping_info(initial_region,
+ &map_info.text_region,
+ &map_info.text_size,
+ &map_info.data_region,
+ &map_info.data_size,
+ &map_info.region_mappings,
+ &map_info.client_base,
+ &map_info.alternate_base,
+ &map_info.alternate_next,
+ &map_info.fs_base,
+ &map_info.system,
+ &map_info.flags,
+ &next);
+ if (map_info.flags & SHARED_REGION_STANDALONE) {
+ /*
+ * We were using a private shared region.
+ * Try and get back to a system-wide shared region
+ * with matching "fs_base" (for chroot) and "system"
+ * (for CPU type).
+ */
+ shared_region = lookup_default_shared_region(
+ map_info.fs_base,
+ map_info.system);
+ if (shared_region == NULL) {
+ /*
+ * No system-wide default regions, stick to
+ * our private region...
+ */
+ } else {
+ SHARED_REGION_TRACE(
+ SHARED_REGION_TRACE_INFO,
+ ("shared_region: %p [%d(%s)] "
+ "exec(\"%s\"): "
+ "moving from private %p[%x,%x,%x] "
+ "to default %p\n",
+ current_thread(),
+ p->p_pid, p->p_comm,
+ (imgp->ip_p_comm[0] ?
+ imgp->ip_p_comm :
+ imgp->ip_ndp->ni_cnd.cn_nameptr),
+ initial_region,
+ map_info.fs_base,
+ map_info.system,
+ map_info.flags,
+ shared_region));
+ vm_set_shared_region(task, shared_region);
+ shared_region_mapping_dealloc(initial_region);
+ }
+ }
+ }
+
+ /*
+ * NOTE: An error after this point indicates we have potentially
+ * destroyed or overwrote some process state while attempting an
+ * execve() following a vfork(), which is an unrecoverable condition.
+ */
+
+ /*
+ * We reset the task to 64-bit (or not) here. It may have picked up
+ * a new map, and we need that to reflect its true 64-bit nature.
+ */
+
+ task_set_64bit(task,
+ ((imgp->ip_flags & IMGPF_IS_64BIT) == IMGPF_IS_64BIT));
+
+ /*
+ * Actually load the image file we previously decided to load.
+ */
+ lret = load_machfile(imgp, mach_header, thread, map, clean_regions, &load_result);
+
+ if (lret != LOAD_SUCCESS) {
+ error = load_return_to_errno(lret);
+ goto badtoolate;
+ }
+
+ /* load_machfile() maps the vnode */
+ (void)ubc_map(imgp->ip_vp, PROT_EXEC);
+
+ /*
+ * deal with set[ug]id.
+ */
+ error = exec_handle_sugid(imgp);
+
+ KNOTE(&p->p_klist, NOTE_EXEC);
+
+ if (!vfexec && (p->p_flag & P_TRACED))
+ psignal(p, SIGTRAP);
+
+ if (error) {
+ goto badtoolate;
+ }
+ vnode_put(imgp->ip_vp);
+ imgp->ip_vp = NULL;
+
+ if (load_result.unixproc &&
+ create_unix_stack(get_task_map(task),
+ load_result.user_stack, load_result.customstack, p)) {
+ error = load_return_to_errno(LOAD_NOSPACE);
+ goto badtoolate;
+ }
+
+ if (vfexec) {
+ old_map = vm_map_switch(get_task_map(task));
+ }
+
+ if (load_result.unixproc) {
+ user_addr_t ap;
+
+ /*
+ * Copy the strings area out into the new process address
+ * space.
+ */
+ ap = p->user_stack;
+ error = exec_copyout_strings(imgp, &ap);
+ if (error) {
+ if (vfexec)
+ vm_map_switch(old_map);
+ goto badtoolate;
+ }
+ /* Set the stack */
+ thread_setuserstack(thread, ap);
+ }
+
+ if (load_result.dynlinker) {
+ uint64_t ap;
+
+ /* Adjust the stack */
+ if (imgp->ip_flags & IMGPF_IS_64BIT) {
+ ap = thread_adjuserstack(thread, -8);
+ error = copyoutptr(load_result.mach_header, ap, 8);
+ } else {
+ ap = thread_adjuserstack(thread, -4);
+ error = suword(ap, load_result.mach_header);
+ }
+ if (error) {
+ if (vfexec)
+ vm_map_switch(old_map);
+ goto badtoolate;
+ }
+ }