+ 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;
+ }
+ }
+
+ if (vfexec) {
+ vm_map_switch(old_map);
+ }
+ /* Set the entry point */
+ thread_setentrypoint(thread, load_result.entry_point);
+
+ /* Stop profiling */
+ stopprofclock(p);
+
+ /*
+ * Reset signal state.
+ */
+ execsigs(p, thread);
+
+ /*
+ * Close file descriptors
+ * which specify close-on-exec.
+ */
+ fdexec(p);
+
+ /*
+ * need to cancel async IO requests that can be cancelled and wait for those
+ * already active. MAY BLOCK!
+ */
+ _aio_exec( p );
+
+ /* FIXME: Till vmspace inherit is fixed: */
+ if (!vfexec && p->vm_shm)
+ shmexec(p);
+ /* Clean up the semaphores */
+ semexit(p);
+
+ /*
+ * Remember file name for accounting.
+ */
+ p->p_acflag &= ~AFORK;
+ /* If the translated name isn't NULL, then we want to use
+ * that translated name as the name we show as the "real" name.
+ * Otherwise, use the name passed into exec.
+ */
+ if (0 != imgp->ip_p_comm[0]) {
+ bcopy((caddr_t)imgp->ip_p_comm, (caddr_t)p->p_comm,
+ sizeof(p->p_comm));
+ } else {
+ if (imgp->ip_ndp->ni_cnd.cn_namelen > MAXCOMLEN)
+ imgp->ip_ndp->ni_cnd.cn_namelen = MAXCOMLEN;
+ bcopy((caddr_t)imgp->ip_ndp->ni_cnd.cn_nameptr, (caddr_t)p->p_comm,
+ (unsigned)imgp->ip_ndp->ni_cnd.cn_namelen);
+ p->p_comm[imgp->ip_ndp->ni_cnd.cn_namelen] = '\0';
+ }
+
+ if (kdebug_enable) {
+ long dbg_arg1, dbg_arg2, dbg_arg3, dbg_arg4;
+
+ /*
+ * Collect the pathname for tracing
+ */
+ kdbg_trace_string(p, &dbg_arg1, &dbg_arg2, &dbg_arg3, &dbg_arg4);
+
+ if (vfexec)
+ {
+ KERNEL_DEBUG_CONSTANT1((TRACEDBG_CODE(DBG_TRACE_DATA, 2)) | DBG_FUNC_NONE,
+ p->p_pid ,0,0,0, (unsigned int)thread);
+ KERNEL_DEBUG_CONSTANT1((TRACEDBG_CODE(DBG_TRACE_STRING, 2)) | DBG_FUNC_NONE,
+ dbg_arg1, dbg_arg2, dbg_arg3, dbg_arg4, (unsigned int)thread);
+ }
+ else
+ {
+ KERNEL_DEBUG_CONSTANT((TRACEDBG_CODE(DBG_TRACE_DATA, 2)) | DBG_FUNC_NONE,
+ p->p_pid ,0,0,0,0);
+ KERNEL_DEBUG_CONSTANT((TRACEDBG_CODE(DBG_TRACE_STRING, 2)) | DBG_FUNC_NONE,
+ dbg_arg1, dbg_arg2, dbg_arg3, dbg_arg4, 0);
+ }
+ }
+
+#ifdef IMGPF_POWERPC
+ /*
+ * Mark the process as powerpc or not. If powerpc, set the affinity
+ * flag, which will be used for grading binaries in future exec's
+ * from the process.
+ */
+ if (((imgp->ip_flags & IMGPF_POWERPC) != 0))
+ p->p_flag |= P_TRANSLATED;
+ else
+#endif /* IMGPF_POWERPC */
+ p->p_flag &= ~P_TRANSLATED;
+ p->p_flag &= ~P_AFFINITY;
+
+ /*
+ * mark as execed, wakeup the process that vforked (if any) and tell
+ * it that it now has it's own resources back
+ */
+ p->p_flag |= P_EXEC;
+ if (p->p_pptr && (p->p_flag & P_PPWAIT)) {
+ p->p_flag &= ~P_PPWAIT;
+ wakeup((caddr_t)p->p_pptr);
+ }
+
+ if (vfexec && (p->p_flag & P_TRACED)) {
+ psignal_vfork(p, new_task, thread, SIGTRAP);
+ }
+
+badtoolate:
+ if (vfexec) {
+ task_deallocate(new_task);
+ thread_deallocate(thread);
+ if (error)
+ error = 0;
+ }
+
+bad:
+ return(error);
+}
+
+
+
+
+/*
+ * Our image activator table; this is the table of the image types we are
+ * capable of loading. We list them in order of preference to ensure the
+ * fastest image load speed.
+ *
+ * XXX hardcoded, for now; should use linker sets
+ */
+struct execsw {
+ int (*ex_imgact)(struct image_params *);
+ const char *ex_name;
+} execsw[] = {
+ { exec_mach_imgact, "Mach-o Binary" },
+ { exec_fat_imgact, "Fat Binary" },
+#ifdef IMGPF_POWERPC
+ { exec_powerpc32_imgact, "PowerPC binary" },
+#endif /* IMGPF_POWERPC */
+ { exec_shell_imgact, "Interpreter Script" },
+ { NULL, NULL}
+};
+
+
+/*
+ * TODO: Dynamic linker header address on stack is copied via suword()
+ */
+/* ARGSUSED */
+int
+execve(struct proc *p, struct execve_args *uap, register_t *retval)
+{
+ kauth_cred_t cred = p->p_ucred;
+ struct image_params image_params, *imgp;
+ struct vnode_attr va;
+ struct vnode_attr origva;
+ struct nameidata nd;
+ struct uthread *uthread;
+ int i;
+ int resid, error;
+ task_t task;
+ int numthreads;
+ int vfexec=0;
+ int once = 1; /* save SGUID-ness for interpreted files */
+ char alt_p_comm[sizeof(p->p_comm)] = {0}; /* for PowerPC */
+ int is_64 = IS_64BIT_PROCESS(p);
+ int seg = (is_64 ? UIO_USERSPACE64 : UIO_USERSPACE32);
+ struct vfs_context context;
+
+ context.vc_proc = p;
+ context.vc_ucred = p->p_ucred; /* XXX must NOT be kauth_cred_get() */
+
+
+ imgp = &image_params;
+
+ /* Initialize the common data in the image_params structure */
+ bzero(imgp, sizeof(*imgp));
+ imgp->ip_user_fname = uap->fname;
+ imgp->ip_user_argv = uap->argp;
+ imgp->ip_user_envv = uap->envp;
+ imgp->ip_vattr = &va;
+ imgp->ip_origvattr = &origva;
+ imgp->ip_vfs_context = &context;
+ imgp->ip_flags = (is_64 ? IMGPF_WAS_64BIT : IMGPF_NONE);
+ imgp->ip_tws_cache_name = NULL;
+ imgp->ip_p_comm = alt_p_comm; /* for PowerPC */
+
+ /*
+ * XXXAUDIT: Currently, we only audit the pathname of the binary.
+ * There may also be poor interaction with dyld.
+ */
+
+ task = current_task();
+ uthread = get_bsdthread_info(current_thread());
+
+ if (uthread->uu_flag & UT_VFORK) {
+ vfexec = 1; /* Mark in exec */
+ } else {
+ if (task != kernel_task) {
+ numthreads = get_task_numacts(task);
+ if (numthreads <= 0 )
+ return(EINVAL);
+ if (numthreads > 1) {
+ return(ENOTSUP);
+ }
+ }
+ }
+
+ error = execargs_alloc(imgp);
+ if (error)
+ return(error);
+
+ /*
+ * XXXAUDIT: Note: the double copyin introduces an audit
+ * race. To correct this race, we must use a single
+ * copyin(), e.g. by passing a flag to namei to indicate an
+ * external path buffer is being used.
+ */
+ error = exec_save_path(imgp, uap->fname, seg);
+ if (error) {
+ execargs_free(imgp);
+ return(error);
+ }
+
+ /*
+ * No app profiles under chroot
+ */
+ if((p->p_fd->fd_rdir == NULLVP) && (app_profile != 0)) {
+
+ /* grab the name of the file out of its path */
+ /* we will need this for lookup within the */
+ /* name file */
+ /* Scan backwards for the first '/' or start of string */
+ imgp->ip_tws_cache_name = imgp->ip_strendp;
+ while (imgp->ip_tws_cache_name[0] != '/') {
+ if(imgp->ip_tws_cache_name == imgp->ip_strings) {
+ imgp->ip_tws_cache_name--;
+ break;
+ }
+ imgp->ip_tws_cache_name--;
+ }
+ imgp->ip_tws_cache_name++;
+ }
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNPATH1,
+ seg, uap->fname, imgp->ip_vfs_context);
+
+again:
+ error = namei(&nd);
+ if (error)
+ goto bad;
+ imgp->ip_ndp = &nd; /* successful namei(); call nameidone() later */
+ imgp->ip_vp = nd.ni_vp; /* if set, need to vnode_put() at some point */
+
+ error = exec_check_permissions(imgp);
+ if (error)
+ goto bad;
+
+ /* Copy; avoid invocation of an interpreter overwriting the original */
+ if (once) {
+ once = 0;
+ origva = va;
+ }
+
+ error = vn_rdwr(UIO_READ, imgp->ip_vp, imgp->ip_vdata, PAGE_SIZE, 0,
+ UIO_SYSSPACE32, IO_NODELOCKED, cred, &resid, p);
+ if (error)
+ goto bad;
+
+encapsulated_binary:
+ error = -1;
+ for(i = 0; error == -1 && execsw[i].ex_imgact != NULL; i++) {
+
+ error = (*execsw[i].ex_imgact)(imgp);
+
+ switch (error) {
+ /* case -1: not claimed: continue */
+ case -2: /* Encapsulated binary */
+ goto encapsulated_binary;
+
+ case -3: /* Interpreter */
+ vnode_put(imgp->ip_vp);
+ imgp->ip_vp = NULL; /* already put */
+ nd.ni_cnd.cn_nameiop = LOOKUP;
+ nd.ni_cnd.cn_flags = (nd.ni_cnd.cn_flags & HASBUF) |
+ (FOLLOW | LOCKLEAF);
+
+#ifdef IMGPF_POWERPC
+ /*
+ * PowerPC does not follow symlinks because the
+ * code which sets exec_archhandler_ppc.fsid and
+ * exec_archhandler_ppc.fileid doesn't follow them.
+ */
+ if (imgp->ip_flags & IMGPF_POWERPC)
+ nd.ni_cnd.cn_flags &= ~FOLLOW;
+#endif /* IMGPF_POWERPC */
+
+ nd.ni_segflg = UIO_SYSSPACE32;
+ nd.ni_dirp = CAST_USER_ADDR_T(imgp->ip_interp_name);
+ goto again;
+
+ default:
+ break;
+ }
+ }
+
+ /* call out to allow 3rd party notification of exec.
+ * Ignore result of kauth_authorize_fileop call.
+ */
+ if (error == 0 && kauth_authorize_fileop_has_listeners()) {
+ kauth_authorize_fileop(vfs_context_ucred(&context), KAUTH_FILEOP_EXEC,
+ (uintptr_t)nd.ni_vp, 0);
+ }
+
+ /* Image not claimed by any activator? */
+ if (error == -1)
+ error = ENOEXEC;
+
+bad:
+ if (imgp->ip_ndp)
+ nameidone(imgp->ip_ndp);
+ if (imgp->ip_vp)
+ vnode_put(imgp->ip_vp);
+ if (imgp->ip_strings)
+ execargs_free(imgp);
+ if (!error && vfexec) {
+ vfork_return(current_thread(), p->p_pptr, p, retval);
+ (void)thread_resume(imgp->ip_vfork_thread);
+ return(0);
+ }
+ return(error);
+}
+
+
+static int
+copyinptr(user_addr_t froma, user_addr_t *toptr, int ptr_size)
+{
+ int error;
+
+ if (ptr_size == 4) {
+ /* 64 bit value containing 32 bit address */
+ unsigned int i;
+
+ error = copyin(froma, &i, 4);
+ *toptr = CAST_USER_ADDR_T(i); /* SAFE */
+ } else {
+ error = copyin(froma, toptr, 8);
+ }
+ return (error);
+}
+
+
+static int
+copyoutptr(user_addr_t ua, user_addr_t ptr, int ptr_size)
+{
+ int error;
+
+ if (ptr_size == 4) {
+ /* 64 bit value containing 32 bit address */
+ unsigned int i = CAST_DOWN(unsigned int,ua); /* SAFE */
+
+ error = copyout(&i, ptr, 4);
+ } else {
+ error = copyout(&ua, ptr, 8);
+ }
+ return (error);
+}
+
+
+/*
+ * exec_copyout_strings
+ *
+ * Copy out the strings segment to user space. The strings segment is put
+ * on a preinitialized stack frame.
+ *
+ * Parameters: struct image_params * the image parameter block
+ * int * a pointer to the stack offset variable
+ *
+ * Returns: 0 Success
+ * !0 Faiure: errno
+ *
+ * Implicit returns:
+ * (*stackp) The stack offset, modified
+ *
+ * Note: The strings segment layout is backward, from the beginning
+ * of the top of the stack to consume the minimal amount of
+ * space possible; the returned stack pointer points to the
+ * end of the area consumed (stacks grow upward).
+ *
+ * argc is an int; arg[i] are pointers; env[i] are pointers;
+ * exec_path is a pointer; the 0's are (void *)NULL's
+ *
+ * The stack frame layout is:
+ *
+ * +-------------+
+ * sp-> | argc |
+ * +-------------+
+ * | arg[0] |
+ * +-------------+
+ * :
+ * :
+ * +-------------+
+ * | arg[argc-1] |
+ * +-------------+
+ * | 0 |
+ * +-------------+
+ * | env[0] |
+ * +-------------+
+ * :
+ * :
+ * +-------------+
+ * | env[n] |
+ * +-------------+
+ * | 0 |
+ * +-------------+
+ * | exec_path | In MacOS X PR2 Beaker2E the path passed to exec() is
+ * +-------------+ passed on the stack just after the trailing 0 of the
+ * | 0 | the envp[] array as a pointer to a string.
+ * +-------------+
+ * | PATH AREA |
+ * +-------------+
+ * | STRING AREA |
+ * :
+ * :
+ * | | <- p->user_stack
+ * +-------------+
+ *
+ * Although technically a part of the STRING AREA, we treat the PATH AREA as
+ * a separate entity. This allows us to align the beginning of the PATH AREA
+ * to a pointer boundary so that the exec_path, env[i], and argv[i] pointers
+ * which preceed it on the stack are properly aligned.
+ *
+ * TODO: argc copied with suword(), which takes a 64 bit address
+ */
+static int
+exec_copyout_strings(struct image_params *imgp, user_addr_t *stackp)
+{
+ struct proc *p = vfs_context_proc(imgp->ip_vfs_context);
+ int ptr_size = (imgp->ip_flags & IMGPF_IS_64BIT) ? 8 : 4;
+ char *argv = imgp->ip_argv; /* modifiable copy of argv */
+ user_addr_t string_area; /* *argv[], *env[] */
+ user_addr_t path_area; /* package launch path */
+ user_addr_t ptr_area; /* argv[], env[], exec_path */
+ user_addr_t stack;
+ int stringc = imgp->ip_argc + imgp->ip_envc;
+ int len;
+ int error;
+ int strspace;
+
+ stack = *stackp;
+
+ unsigned patharea_len = imgp->ip_argv - imgp->ip_strings;
+ int envc_add = 0;
+
+#ifdef IMGPF_POWERPC
+ /*
+ * oah750 expects /usr/lib/dyld\0 as the start of the program name.
+ * It also expects to have a certain environment variable set to 0.
+ * 50 bytes for each to ensure we have enough space without having
+ * to count every byte.
+ */
+ char *progname, *envvar;
+ char progname_str[] = "/usr/lib/dyld";
+ char envvar_str[] = "OAH750_CFG_FU_STACK_SIZE=0";
+
+ if (imgp->ip_flags & IMGPF_POWERPC) {
+ progname = progname_str;
+ envvar = envvar_str;
+ patharea_len += strlen(progname) + strlen(envvar) + 2;
+ envc_add = 1;
+ }
+#endif /* IMGPF_POWERPC */
+ /*
+ * Set up pointers to the beginning of the string area, the beginning
+ * of the path area, and the beginning of the pointer area (actually,
+ * the location of argc, an int, which may be smaller than a pointer,
+ * but we use ptr_size worth of space for it, for alignment).
+ */
+ string_area = stack - (((imgp->ip_strendp - imgp->ip_strings) + ptr_size-1) & ~(ptr_size-1)) - ptr_size;
+ path_area = string_area - ((patharea_len + ptr_size-1) & ~(ptr_size-1));
+ ptr_area = path_area - ((imgp->ip_argc + imgp->ip_envc + 4 + envc_add) * ptr_size) - ptr_size /*argc*/;
+
+ /* Return the initial stack address: the location of argc */
+ *stackp = ptr_area;
+
+ /*
+ * Record the size of the arguments area so that sysctl_procargs()
+ * can return the argument area without having to parse the arguments.
+ */
+ p->p_argc = imgp->ip_argc;
+ p->p_argslen = (int)(stack - path_area);
+
+
+ /*
+ * Support for new app package launching for Mac OS X allocates
+ * the "path" at the begining of the imgp->ip_strings buffer.
+ * copy it just before the string area.
+ */
+ len = 0;
+#ifdef IMGPF_POWERPC
+ if (imgp->ip_flags & IMGPF_POWERPC) {
+ error = copyoutstr(progname, path_area,
+ patharea_len,
+ (size_t *)&len);
+ if (error)
+ goto bad;
+ error = copyoutstr(imgp->ip_strings, path_area + strlen(progname) + 1,
+ patharea_len,
+ (size_t *)&len);
+ } else
+#endif /* IMGPF_POWERPC */
+ error = copyoutstr(imgp->ip_strings, path_area,
+ patharea_len,
+ (size_t *)&len);
+ if (error)
+ goto bad;
+
+
+ /* Save a NULL pointer below it */
+ (void)copyoutptr(0LL, path_area - ptr_size, ptr_size);
+
+ /* Save the pointer to "path" just below it */
+ (void)copyoutptr(path_area, path_area - 2*ptr_size, ptr_size);
+
+ /*
+ * ptr_size for 2 NULL one each ofter arg[argc -1] and env[n]
+ * ptr_size for argc
+ * skip over saved path, ptr_size for pointer to path,
+ * and ptr_size for the NULL after pointer to path.
+ */
+
+ /* argc (int32, stored in a ptr_size area) */
+ (void)suword(ptr_area, imgp->ip_argc);
+ ptr_area += sizeof(int);
+ /* pad to ptr_size, if 64 bit image, to ensure user stack alignment */
+ if (imgp->ip_flags & IMGPF_IS_64BIT) {
+ (void)suword(ptr_area, 0); /* int, not long: ignored */
+ ptr_area += sizeof(int);
+ }
+
+
+ /*
+ * We use (string_area - path_area) here rather than the more
+ * intuitive (imgp->ip_argv - imgp->ip_strings) because we are
+ * interested in the length of the PATH_AREA in user space,
+ * rather than the actual length of the execution path, since
+ * it includes alignment padding of the PATH_AREA + STRING_AREA
+ * to a ptr_size boundary.
+ */
+ strspace = SIZE_IMG_STRSPACE - (string_area - path_area);
+ for (;;) {
+ if (stringc == imgp->ip_envc) {
+ /* argv[n] = NULL */
+ (void)copyoutptr(0LL, ptr_area, ptr_size);
+ ptr_area += ptr_size;
+#ifdef IMGPF_POWERPC
+ if (envc_add) {
+ (void)copyoutptr(string_area, ptr_area, ptr_size);
+
+ do {
+ if (strspace <= 0) {
+ error = E2BIG;
+ break;
+ }
+ error = copyoutstr(envvar, string_area,
+ (unsigned)strspace,
+ (size_t *)&len);
+ string_area += len;
+ envvar += len;
+ strspace -= len;
+ } while (error == ENAMETOOLONG);
+ if (error == EFAULT || error == E2BIG)
+ break;
+ ptr_area += ptr_size;
+ }
+#endif /* IMGPF_POWERPC */
+ }
+ if (--stringc < 0)
+ break;
+
+ /* pointer: argv[n]/env[n] */
+ (void)copyoutptr(string_area, ptr_area, ptr_size);
+
+ /* string : argv[n][]/env[n][] */
+ do {
+ if (strspace <= 0) {
+ error = E2BIG;
+ break;
+ }
+ error = copyoutstr(argv, string_area,
+ (unsigned)strspace,
+ (size_t *)&len);
+ string_area += len;
+ argv += len;
+ strspace -= len;
+ } while (error == ENAMETOOLONG);
+ if (error == EFAULT || error == E2BIG)
+ break; /* bad stack - user's problem */
+ ptr_area += ptr_size;
+ }
+ /* env[n] = NULL */
+ (void)copyoutptr(0LL, ptr_area, ptr_size);
+
+bad:
+ return(error);
+}
+
+
+/*
+ * exec_extract_strings
+ *
+ * Copy arguments and environment from user space into work area; we may
+ * have already copied some early arguments into the work area, and if
+ * so, any arguments opied in are appended to those already there.
+ *
+ * Parameters: struct image_params * the image parameter block
+ *
+ * Returns: 0 Success
+ * !0 Failure: errno
+ *
+ * Implicit returns;
+ * (imgp->ip_argc) Count of arguments, updated
+ * (imgp->ip_envc) Count of environment strings, updated
+ *
+ *
+ * Notes: The argument and environment vectors are user space pointers
+ * to arrays of user space pointers.
+ */
+static int
+exec_extract_strings(struct image_params *imgp)
+{
+ int error = 0;
+ struct proc *p = vfs_context_proc(imgp->ip_vfs_context);
+ int seg = (IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32);
+ int ptr_size = (imgp->ip_flags & IMGPF_WAS_64BIT) ? 8 : 4;
+ user_addr_t argv = imgp->ip_user_argv;
+ user_addr_t envv = imgp->ip_user_envv;