Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files. =================================================================== RCS file: /ftp/cvs/cvsroot/src/sys/kern/kern_exec.c,v rcsdiff: /ftp/cvs/cvsroot/src/sys/kern/kern_exec.c,v: warning: Unknown phrases like `commitid ...;' are present. retrieving revision 1.103 retrieving revision 1.103.2.6 diff -u -p -r1.103 -r1.103.2.6 --- src/sys/kern/kern_exec.c 1999/09/28 14:47:03 1.103 +++ src/sys/kern/kern_exec.c 2001/02/11 19:16:45 1.103.2.6 @@ -1,4 +1,4 @@ -/* $NetBSD: kern_exec.c,v 1.103 1999/09/28 14:47:03 bouyer Exp $ */ +/* $NetBSD: kern_exec.c,v 1.103.2.6 2001/02/11 19:16:45 bouyer Exp $ */ /*- * Copyright (C) 1993, 1994, 1996 Christopher G. Demetriou @@ -33,6 +33,7 @@ */ #include "opt_ktrace.h" +#include "opt_syscall_debug.h" #include #include @@ -52,18 +53,107 @@ #include #include #include +#include #include -#include -#include - #include #include #include /* + * Exec function switch: + * + * Note that each makecmds function is responsible for loading the + * exec package with the necessary functions for any exec-type-specific + * handling. + * + * Functions for specific exec types should be defined in their own + * header file. + */ +extern const struct execsw execsw_builtin[]; +extern int nexecs_builtin; +static const struct execsw **execsw = NULL; +static int nexecs; +int exec_maxhdrsz; /* must not be static - netbsd32 needs it */ + +#ifdef LKM +/* list of supported emulations */ +static +LIST_HEAD(emlist_head, emul_entry) el_head = LIST_HEAD_INITIALIZER(el_head); +struct emul_entry { + LIST_ENTRY(emul_entry) el_list; + const struct emul *el_emul; + int ro_entry; +}; + +/* list of dynamically loaded execsw entries */ +static +LIST_HEAD(execlist_head, exec_entry) ex_head = LIST_HEAD_INITIALIZER(ex_head); +struct exec_entry { + LIST_ENTRY(exec_entry) ex_list; + const struct execsw *es; +}; + +/* structure used for building execw[] */ +struct execsw_entry { + struct execsw_entry *next; + const struct execsw *es; +}; +#endif /* LKM */ + +/* NetBSD emul struct */ +extern char sigcode[], esigcode[]; +#ifdef SYSCALL_DEBUG +extern const char * const syscallnames[]; +#endif +#ifdef __HAVE_SYSCALL_INTERN +void syscall_intern __P((struct proc *)); +#else +void syscall __P((void)); +#endif + +const struct emul emul_netbsd = { + "netbsd", + NULL, /* emulation path */ +#ifndef __HAVE_MINIMAL_EMUL + EMUL_HAS_SYS___syscall, + NULL, + SYS_syscall, + SYS_MAXSYSCALL, +#endif + sysent, +#ifdef SYSCALL_DEBUG + syscallnames, +#else + NULL, +#endif + sendsig, + sigcode, + esigcode, + NULL, + NULL, + NULL, +#ifdef __HAVE_SYSCALL_INTERN + syscall_intern, +#else + syscall, +#endif +}; + +/* + * Exec lock. Used to control access to execsw[] structures. + * This must not be static so that netbsd32 can access it, too. + */ +struct lock exec_lock; + +#ifdef LKM +static const struct emul * emul_search __P((const char *)); +static void link_es __P((struct execsw_entry **, const struct execsw *)); +#endif /* LKM */ + +/* * check exec: * given an "executable" described in the exec package's namei info, * see what we can do with it. @@ -89,9 +179,7 @@ * exec header unmodified. */ int -check_exec(p, epp) - struct proc *p; - struct exec_package *epp; +check_exec(struct proc *p, struct exec_package *epp) { int error, i; struct vnode *vp; @@ -134,6 +222,7 @@ check_exec(p, epp) VOP_UNLOCK(vp, 0); /* now we have the file, get the exec header */ + uvn_attach(vp, VM_PROT_READ); error = vn_rdwr(UIO_READ, vp, epp->ep_hdr, epp->ep_hdrlen, 0, UIO_SYSSPACE, 0, p->p_ucred, &resid, p); if (error) @@ -148,13 +237,16 @@ check_exec(p, epp) for (i = 0; i < nexecs && error != 0; i++) { int newerror; - if (execsw[i].es_check == NULL) - continue; - - newerror = (*execsw[i].es_check)(p, epp); + epp->ep_esch = execsw[i]; + newerror = (*execsw[i]->es_check)(p, epp); /* make sure the first "interesting" error code is saved. */ if (!newerror || error == ENOEXEC) error = newerror; + + /* if es_check call was successful, update epp->ep_es */ + if (!newerror && (epp->ep_flags & EXEC_HASES) == 0) + epp->ep_es = execsw[i]; + if (epp->ep_flags & EXEC_DESTR && error != 0) return error; } @@ -186,7 +278,7 @@ bad2: vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); VOP_CLOSE(vp, FREAD, p->p_ucred, p); vput(vp); - FREE(ndp->ni_cnd.cn_pnbuf, M_NAMEI); + PNBUF_PUT(ndp->ni_cnd.cn_pnbuf); return error; bad1: @@ -195,7 +287,7 @@ bad1: * (which we don't yet have open). */ vput(vp); /* was still locked */ - FREE(ndp->ni_cnd.cn_pnbuf, M_NAMEI); + PNBUF_PUT(ndp->ni_cnd.cn_pnbuf); return error; } @@ -204,12 +296,9 @@ bad1: */ /* ARGSUSED */ int -sys_execve(p, v, retval) - register struct proc *p; - void *v; - register_t *retval; +sys_execve(struct proc *p, void *v, register_t *retval) { - register struct sys_execve_args /* { + struct sys_execve_args /* { syscallarg(const char *) path; syscallarg(char * const *) argp; syscallarg(char * const *) envp; @@ -229,29 +318,22 @@ sys_execve(p, v, retval) struct vmspace *vm; char **tmpfap; int szsigcode; - extern struct emul emul_netbsd; + struct exec_vmcmd *base_vcp = NULL; /* - * figure out the maximum size of an exec header, if necessary. - * XXX should be able to keep LKM code from modifying exec switch - * when we're still using it, but... + * Init the namei data to point the file user's program name. + * This is done here rather than in check_exec(), so that it's + * possible to override this settings if any of makecmd/probe + * functions call check_exec() recursively - for example, + * see exec_script_makecmds(). */ - if (exec_maxhdrsz == 0) { - for (i = 0; i < nexecs; i++) - if (execsw[i].es_check != NULL - && execsw[i].es_hdrsz > exec_maxhdrsz) - exec_maxhdrsz = execsw[i].es_hdrsz; - } - - /* init the namei data to point the file user's program name */ - /* XXX cgd 960926: why do this here? most will be clobbered. */ NDINIT(&nid, LOOKUP, NOFOLLOW, UIO_USERSPACE, SCARG(uap, path), p); /* * initialize the fields of the exec package. */ pack.ep_name = SCARG(uap, path); - MALLOC(pack.ep_hdr, void *, exec_maxhdrsz, M_EXEC, M_WAITOK); + pack.ep_hdr = malloc(exec_maxhdrsz, M_EXEC, M_WAITOK); pack.ep_hdrlen = exec_maxhdrsz; pack.ep_hdrvalid = 0; pack.ep_ndp = &nid; @@ -259,9 +341,10 @@ sys_execve(p, v, retval) pack.ep_vmcmds.evs_cnt = 0; pack.ep_vmcmds.evs_used = 0; pack.ep_vap = &attr; - pack.ep_emul = &emul_netbsd; pack.ep_flags = 0; + lockmgr(&exec_lock, LK_SHARED, NULL); + /* see if we can run it. */ if ((error = check_exec(p, &pack)) != 0) goto freehdr; @@ -342,12 +425,18 @@ sys_execve(p, v, retval) dp = (char *) ALIGN(dp); - szsigcode = pack.ep_emul->e_esigcode - pack.ep_emul->e_sigcode; + szsigcode = pack.ep_es->es_emul->e_esigcode - + pack.ep_es->es_emul->e_sigcode; /* Now check if args & environ fit into new stack */ - len = ((argc + envc + 2 + pack.ep_emul->e_arglen) * sizeof(char *) + - sizeof(long) + dp + STACKGAPLEN + szsigcode + - sizeof(struct ps_strings)) - argp; + if (pack.ep_flags & EXEC_32) + len = ((argc + envc + 2 + pack.ep_es->es_arglen) * + sizeof(int) + sizeof(int) + dp + STACKGAPLEN + + szsigcode + sizeof(struct ps_strings)) - argp; + else + len = ((argc + envc + 2 + pack.ep_es->es_arglen) * + sizeof(char *) + sizeof(int) + dp + STACKGAPLEN + + szsigcode + sizeof(struct ps_strings)) - argp; len = ALIGN(len); /* make the stack "safely" aligned */ @@ -364,7 +453,7 @@ sys_execve(p, v, retval) * for remapping. Note that this might replace the current * vmspace with another! */ - uvmspace_exec(p); + uvmspace_exec(p, VM_MIN_ADDRESS, (vaddr_t)pack.ep_minsaddr); /* Now map address space */ vm = p->p_vmspace; @@ -374,6 +463,7 @@ sys_execve(p, v, retval) vm->vm_dsize = btoc(pack.ep_dsize); vm->vm_ssize = btoc(pack.ep_ssize); vm->vm_maxsaddr = (char *) pack.ep_maxsaddr; + vm->vm_minsaddr = (char *) pack.ep_minsaddr; /* create the new process's VM space by running the vmcmds */ #ifdef DIAGNOSTIC @@ -384,35 +474,84 @@ sys_execve(p, v, retval) struct exec_vmcmd *vcp; vcp = &pack.ep_vmcmds.evs_cmds[i]; + if (vcp->ev_flags & VMCMD_RELATIVE) { +#ifdef DIAGNOSTIC + if (base_vcp == NULL) + panic("execve: relative vmcmd with no base"); + if (vcp->ev_flags & VMCMD_BASE) + panic("execve: illegal base & relative vmcmd"); +#endif + vcp->ev_addr += base_vcp->ev_addr; + } error = (*vcp->ev_proc)(p, vcp); +#ifdef DEBUG + if (error) { + if (i > 0) + printf("vmcmd[%d] = %#lx/%#lx @ %#lx\n", i-1, + vcp[-1].ev_addr, vcp[-1].ev_len, + vcp[-1].ev_offset); + printf("vmcmd[%d] = %#lx/%#lx @ %#lx\n", i, + vcp->ev_addr, vcp->ev_len, vcp->ev_offset); + } +#endif + if (vcp->ev_flags & VMCMD_BASE) + base_vcp = vcp; } /* free the vmspace-creation commands, and release their references */ kill_vmcmds(&pack.ep_vmcmds); /* if an error happened, deallocate and punt */ - if (error) + if (error) { +#ifdef DEBUG + printf("execve: vmcmd %i failed: %d\n", i-1, error); +#endif goto exec_abort; + } /* remember information about the process */ arginfo.ps_nargvstr = argc; arginfo.ps_nenvstr = envc; - stack = (char *) (USRSTACK - len); + stack = (char *) (vm->vm_minsaddr - len); /* Now copy argc, args & environ to new stack */ - if (!(*pack.ep_emul->e_copyargs)(&pack, &arginfo, stack, argp)) + if (!(*pack.ep_es->es_copyargs)(&pack, &arginfo, stack, argp)) { +#ifdef DEBUG + printf("execve: copyargs failed\n"); +#endif goto exec_abort; + } + + /* fill process ps_strings info */ + p->p_psstr = (struct ps_strings *)(vm->vm_minsaddr + - sizeof(struct ps_strings)); + p->p_psargv = offsetof(struct ps_strings, ps_argvstr); + p->p_psnargv = offsetof(struct ps_strings, ps_nargvstr); + p->p_psenv = offsetof(struct ps_strings, ps_envstr); + p->p_psnenv = offsetof(struct ps_strings, ps_nenvstr); /* copy out the process's ps_strings structure */ - if (copyout(&arginfo, (char *) PS_STRINGS, sizeof(arginfo))) + if (copyout(&arginfo, (char *)p->p_psstr, sizeof(arginfo))) { +#ifdef DEBUG + printf("execve: ps_strings copyout failed\n"); +#endif goto exec_abort; + } /* copy out the process's signal trapoline code */ if (szsigcode) { - if (copyout((char *)pack.ep_emul->e_sigcode, - p->p_sigacts->ps_sigcode = (char *)PS_STRINGS - szsigcode, - szsigcode)) + if (copyout((char *)pack.ep_es->es_emul->e_sigcode, + p->p_sigctx.ps_sigcode = (char *)p->p_psstr - szsigcode, + szsigcode)) { +#ifdef DEBUG + printf("execve: sig trampoline copyout failed\n"); +#endif goto exec_abort; + } +#ifdef PMAP_NEED_PROCWR + /* This is code. Let the pmap do what is needed. */ + pmap_procwr(p, (vaddr_t)p->p_sigctx.ps_sigcode, szsigcode); +#endif } stopprofclock(p); /* stop profiling */ @@ -463,27 +602,56 @@ sys_execve(p, v, retval) p->p_cred->p_svuid = p->p_ucred->cr_uid; p->p_cred->p_svgid = p->p_ucred->cr_gid; + doexechooks(p); + uvm_km_free_wakeup(exec_map, (vaddr_t) argp, NCARGS); - FREE(nid.ni_cnd.cn_pnbuf, M_NAMEI); + PNBUF_PUT(nid.ni_cnd.cn_pnbuf); vn_lock(pack.ep_vp, LK_EXCLUSIVE | LK_RETRY); VOP_CLOSE(pack.ep_vp, FREAD, cred, p); vput(pack.ep_vp); /* setup new registers and do misc. setup. */ - (*pack.ep_emul->e_setregs)(p, &pack, (u_long) stack); + (*pack.ep_es->es_setregs)(p, &pack, (u_long) stack); if (p->p_flag & P_TRACED) psignal(p, SIGTRAP); - p->p_emul = pack.ep_emul; - FREE(pack.ep_hdr, M_EXEC); + free(pack.ep_hdr, M_EXEC); + + /* + * Call emulation specific exec hook. This can setup setup per-process + * p->p_emuldata or do any other per-process stuff an emulation needs. + * + * If we are executing process of different emulation than the + * original forked process, call e_proc_exit() of the old emulation + * first, then e_proc_exec() of new emulation. If the emulation is + * same, the exec hook code should deallocate any old emulation + * resources held previously by this process. + */ + if (p->p_emul && p->p_emul->e_proc_exit + && p->p_emul != pack.ep_es->es_emul) + (*p->p_emul->e_proc_exit)(p); + /* + * Call exec hook. Emulation code may NOT store reference to anything + * from &pack. + */ + if (pack.ep_es->es_emul->e_proc_exec) + (*pack.ep_es->es_emul->e_proc_exec)(p, &pack); + + /* update p_emul, the old value is no longer needed */ + p->p_emul = pack.ep_es->es_emul; +#ifdef __HAVE_SYSCALL_INTERN + (*p->p_emul->e_syscall_intern)(p); +#endif #ifdef KTRACE if (KTRPOINT(p, KTR_EMUL)) - ktremul(p->p_tracep, p, p->p_emul->e_name); + ktremul(p); #endif + lockmgr(&exec_lock, LK_RELEASE, NULL); + return (EJUSTRETURN); bad: @@ -498,14 +666,18 @@ bad: vn_lock(pack.ep_vp, LK_EXCLUSIVE | LK_RETRY); VOP_CLOSE(pack.ep_vp, FREAD, cred, p); vput(pack.ep_vp); - FREE(nid.ni_cnd.cn_pnbuf, M_NAMEI); + PNBUF_PUT(nid.ni_cnd.cn_pnbuf); uvm_km_free_wakeup(exec_map, (vaddr_t) argp, NCARGS); freehdr: - FREE(pack.ep_hdr, M_EXEC); + lockmgr(&exec_lock, LK_RELEASE, NULL); + + free(pack.ep_hdr, M_EXEC); return error; exec_abort: + lockmgr(&exec_lock, LK_RELEASE, NULL); + /* * the old process doesn't exist anymore. exit gracefully. * get rid of the (new) address space we have created, if any, get rid @@ -515,12 +687,12 @@ exec_abort: VM_MAXUSER_ADDRESS - VM_MIN_ADDRESS); if (pack.ep_emul_arg) FREE(pack.ep_emul_arg, M_TEMP); - FREE(nid.ni_cnd.cn_pnbuf, M_NAMEI); + PNBUF_PUT(nid.ni_cnd.cn_pnbuf); vn_lock(pack.ep_vp, LK_EXCLUSIVE | LK_RETRY); VOP_CLOSE(pack.ep_vp, FREAD, cred, p); vput(pack.ep_vp); uvm_km_free_wakeup(exec_map, (vaddr_t) argp, NCARGS); - FREE(pack.ep_hdr, M_EXEC); + free(pack.ep_hdr, M_EXEC); exit1(p, W_EXITCODE(0, SIGABRT)); exit1(p, -1); @@ -530,23 +702,28 @@ exec_abort: void * -copyargs(pack, arginfo, stack, argp) - struct exec_package *pack; - struct ps_strings *arginfo; - void *stack; - void *argp; +copyargs(struct exec_package *pack, struct ps_strings *arginfo, + void *stack, void *argp) { char **cpp = stack; char *dp, *sp; size_t len; void *nullp = NULL; - int argc = arginfo->ps_nargvstr; - int envc = arginfo->ps_nenvstr; + long argc = arginfo->ps_nargvstr; + long envc = arginfo->ps_nenvstr; +#ifdef __sparc_v9__ + /* XXX Temporary hack for argc format conversion. */ + argc <<= 32; +#endif if (copyout(&argc, cpp++, sizeof(argc))) return NULL; +#ifdef __sparc_v9__ + /* XXX Temporary hack for argc format conversion. */ + argc >>= 32; +#endif - dp = (char *) (cpp + argc + envc + 2 + pack->ep_emul->e_arglen); + dp = (char *) (cpp + argc + envc + 2 + pack->ep_es->es_arglen); sp = argp; /* XXX don't copy them out, remap them! */ @@ -572,3 +749,358 @@ copyargs(pack, arginfo, stack, argp) return cpp; } + +#ifdef LKM +/* + * Find an emulation of given name in list of emulations. + */ +static const struct emul * +emul_search(name) + const char *name; +{ + struct emul_entry *it; + + LIST_FOREACH(it, &el_head, el_list) { + if (strcmp(name, it->el_emul->e_name) == 0) + return it->el_emul; + } + + return NULL; +} + +/* + * Add an emulation to list, if it's not there already. + */ +int +emul_register(emul, ro_entry) + const struct emul *emul; + int ro_entry; +{ + struct emul_entry *ee; + int error = 0; + + lockmgr(&exec_lock, LK_SHARED, NULL); + + if (emul_search(emul->e_name)) { + error = EEXIST; + goto out; + } + + MALLOC(ee, struct emul_entry *, sizeof(struct emul_entry), + M_EXEC, M_WAITOK); + ee->el_emul = emul; + ee->ro_entry = ro_entry; + LIST_INSERT_HEAD(&el_head, ee, el_list); + + out: + lockmgr(&exec_lock, LK_RELEASE, NULL); + return error; +} + +/* + * Remove emulation with name 'name' from list of supported emulations. + */ +int +emul_unregister(name) + const char *name; +{ + struct emul_entry *it; + int i, error = 0; + const struct proclist_desc *pd; + struct proc *ptmp; + + lockmgr(&exec_lock, LK_SHARED, NULL); + + LIST_FOREACH(it, &el_head, el_list) { + if (strcmp(it->el_emul->e_name, name) == 0) + break; + } + + if (!it) { + error = ENOENT; + goto out; + } + + if (it->ro_entry) { + error = EBUSY; + goto out; + } + + /* test if any execw[] entry is still using this */ + for(i=0; i < nexecs; i++) { + if (execsw[i]->es_emul == it->el_emul) { + error = EBUSY; + goto out; + } + } + + /* + * Test if any process is running under this emulation - since + * emul_unregister() is running quite sendomly, it's better + * to do expensive check here than to use any locking. + */ + proclist_lock_read(); + for (pd = proclists; pd->pd_list != NULL && !error; pd++) { + LIST_FOREACH(ptmp, pd->pd_list, p_list) { + if (ptmp->p_emul == it->el_emul) { + error = EBUSY; + break; + } + } + } + proclist_unlock_read(); + + if (error) + goto out; + + + /* entry is not used, remove it */ + LIST_REMOVE(it, el_list); + FREE(it, M_EXEC); + + out: + lockmgr(&exec_lock, LK_RELEASE, NULL); + return error; +} + +/* + * Add execsw[] entry. + */ +int +exec_add(esp, e_name) + struct execsw *esp; + const char *e_name; +{ + struct exec_entry *it; + int error = 0; + + lockmgr(&exec_lock, LK_EXCLUSIVE, NULL); + + if (!esp->es_emul) { + esp->es_emul = emul_search(e_name); + if (!esp->es_emul) { + error = ENOENT; + goto out; + } + } + + LIST_FOREACH(it, &ex_head, ex_list) { + /* assume tuple (makecmds, probe_func, emulation) is unique */ + if (it->es->es_check == esp->es_check + && it->es->u.elf_probe_func == esp->u.elf_probe_func + && it->es->es_emul == esp->es_emul) { + error = EEXIST; + goto out; + } + } + + /* if we got here, the entry doesn't exist yet */ + MALLOC(it, struct exec_entry *, sizeof(struct exec_entry), + M_EXEC, M_WAITOK); + it->es = esp; + LIST_INSERT_HEAD(&ex_head, it, ex_list); + + /* update execsw[] */ + exec_init(0); + + out: + lockmgr(&exec_lock, LK_RELEASE, NULL); + return error; +} + +/* + * Remove execsw[] entry. + */ +int +exec_remove(esp) + const struct execsw *esp; +{ + struct exec_entry *it; + int error = 0; + + lockmgr(&exec_lock, LK_EXCLUSIVE, NULL); + + LIST_FOREACH(it, &ex_head, ex_list) { + /* assume tuple (makecmds, probe_func, emulation) is unique */ + if (it->es->es_check == esp->es_check + && it->es->u.elf_probe_func == esp->u.elf_probe_func + && it->es->es_emul == esp->es_emul) + break; + } + if (!it) { + error = ENOENT; + goto out; + } + + /* remove item from list and free resources */ + LIST_REMOVE(it, ex_list); + FREE(it, M_EXEC); + + /* update execsw[] */ + exec_init(0); + + out: + lockmgr(&exec_lock, LK_RELEASE, NULL); + return error; +} + +static void +link_es(listp, esp) + struct execsw_entry **listp; + const struct execsw *esp; +{ + struct execsw_entry *et, *e1; + + MALLOC(et, struct execsw_entry *, sizeof(struct execsw_entry), + M_TEMP, M_WAITOK); + et->next = NULL; + et->es = esp; + if (*listp == NULL) { + *listp = et; + return; + } + + switch(et->es->es_prio) { + case EXECSW_PRIO_FIRST: + /* put new entry as the first */ + et->next = *listp; + *listp = et; + break; + case EXECSW_PRIO_ANY: + /* put new entry after all *_FIRST and *_ANY entries */ + for(e1 = *listp; e1->next + && e1->next->es->es_prio != EXECSW_PRIO_LAST; + e1 = e1->next); + et->next = e1->next; + e1->next = et; + break; + case EXECSW_PRIO_LAST: + /* put new entry as the last one */ + for(e1 = *listp; e1->next; e1 = e1->next); + e1->next = et; + break; + default: +#ifdef DIAGNOSTIC + panic("execw[] entry with unknown priority %d found\n", + et->es->es_prio); +#endif + break; + } +} + +/* + * Initialize exec structures. If init_boot is true, also does necessary + * one-time initialization (it's called from main() that way). + * Once system is multiuser, this should be called with exec_lock hold, + * i.e. via exec_{add|remove}(). + */ +int +exec_init(init_boot) + int init_boot; +{ + const struct execsw **new_es, * const *old_es; + struct execsw_entry *list, *e1; + struct exec_entry *e2; + int i, es_sz; + + if (init_boot) { + /* do one-time initializations */ + lockinit(&exec_lock, PWAIT, "execlck", 0, 0); + + /* register compiled-in emulations */ + for(i=0; i < nexecs_builtin; i++) { + if (execsw_builtin[i].es_emul) + emul_register(execsw_builtin[i].es_emul, 1); + } +#ifdef DIAGNOSTIC + if (i == 0) + panic("no emulations found in execsw_builtin[]\n"); +#endif + } + + /* + * Build execsw[] array from builtin entries and entries added + * at runtime. + */ + list = NULL; + for(i=0; i < nexecs_builtin; i++) + link_es(&list, &execsw_builtin[i]); + + /* Add dynamically loaded entries */ + es_sz = nexecs_builtin; + LIST_FOREACH(e2, &ex_head, ex_list) { + link_es(&list, e2->es); + es_sz++; + } + + /* + * Now that we have sorted all execw entries, create new execsw[] + * and free no longer needed memory in the process. + */ + new_es = malloc(es_sz * sizeof(struct execsw *), M_EXEC, M_WAITOK); + for(i=0; list; i++) { + new_es[i] = list->es; + e1 = list->next; + FREE(list, M_TEMP); + list = e1; + } + + /* + * New execsw[] array built, now replace old execsw[] and free + * used memory. + */ + old_es = execsw; + execsw = new_es; + nexecs = es_sz; + if (old_es) + free((void *)old_es, M_EXEC); + + /* + * Figure out the maximum size of an exec header. + */ + exec_maxhdrsz = 0; + for (i = 0; i < nexecs; i++) { + if (execsw[i]->es_hdrsz > exec_maxhdrsz) + exec_maxhdrsz = execsw[i]->es_hdrsz; + } + + return 0; +} +#endif + +#ifndef LKM +/* + * Simplified exec_init() for kernels without LKMs. Only initialize + * exec_maxhdrsz and execsw[]. + */ +int +exec_init(init_boot) + int init_boot; +{ + int i; + +#ifdef DIAGNOSTIC + if (!init_boot) + panic("exec_init(): called with init_boot == 0"); +#endif + + /* do one-time initializations */ + lockinit(&exec_lock, PWAIT, "execlck", 0, 0); + + nexecs = nexecs_builtin; + execsw = malloc(nexecs*sizeof(struct execsw *), M_EXEC, M_WAITOK); + + /* + * Fill in execsw[] and figure out the maximum size of an exec header. + */ + exec_maxhdrsz = 0; + for(i=0; i < nexecs; i++) { + execsw[i] = &execsw_builtin[i]; + if (execsw_builtin[i].es_hdrsz > exec_maxhdrsz) + exec_maxhdrsz = execsw_builtin[i].es_hdrsz; + } + + return 0; + +} +#endif /* !LKM */