version 1.103, 1999/09/28 14:47:03 |
version 1.103.2.4, 2000/12/13 15:50:20 |
|
|
*/ |
*/ |
|
|
#include "opt_ktrace.h" |
#include "opt_ktrace.h" |
|
#include "opt_syscall_debug.h" |
|
|
#include <sys/param.h> |
#include <sys/param.h> |
#include <sys/systm.h> |
#include <sys/systm.h> |
|
|
#include <sys/mman.h> |
#include <sys/mman.h> |
#include <sys/signalvar.h> |
#include <sys/signalvar.h> |
#include <sys/stat.h> |
#include <sys/stat.h> |
|
#include <sys/syscall.h> |
|
|
#include <sys/syscallargs.h> |
#include <sys/syscallargs.h> |
|
|
#include <vm/vm.h> |
|
#include <vm/vm_kern.h> |
|
|
|
#include <uvm/uvm_extern.h> |
#include <uvm/uvm_extern.h> |
|
|
#include <machine/cpu.h> |
#include <machine/cpu.h> |
#include <machine/reg.h> |
#include <machine/reg.h> |
|
|
/* |
/* |
|
* 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: |
* check exec: |
* given an "executable" described in the exec package's namei info, |
* given an "executable" described in the exec package's namei info, |
* see what we can do with it. |
* see what we can do with it. |
|
|
* exec header unmodified. |
* exec header unmodified. |
*/ |
*/ |
int |
int |
check_exec(p, epp) |
check_exec(struct proc *p, struct exec_package *epp) |
struct proc *p; |
|
struct exec_package *epp; |
|
{ |
{ |
int error, i; |
int error, i; |
struct vnode *vp; |
struct vnode *vp; |
Line 134 check_exec(p, epp) |
|
Line 222 check_exec(p, epp) |
|
VOP_UNLOCK(vp, 0); |
VOP_UNLOCK(vp, 0); |
|
|
/* now we have the file, get the exec header */ |
/* 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, |
error = vn_rdwr(UIO_READ, vp, epp->ep_hdr, epp->ep_hdrlen, 0, |
UIO_SYSSPACE, 0, p->p_ucred, &resid, p); |
UIO_SYSSPACE, 0, p->p_ucred, &resid, p); |
if (error) |
if (error) |
Line 148 check_exec(p, epp) |
|
Line 237 check_exec(p, epp) |
|
for (i = 0; i < nexecs && error != 0; i++) { |
for (i = 0; i < nexecs && error != 0; i++) { |
int newerror; |
int newerror; |
|
|
if (execsw[i].es_check == NULL) |
epp->ep_esch = execsw[i]; |
continue; |
newerror = (*execsw[i]->es_check)(p, epp); |
|
|
newerror = (*execsw[i].es_check)(p, epp); |
|
/* make sure the first "interesting" error code is saved. */ |
/* make sure the first "interesting" error code is saved. */ |
if (!newerror || error == ENOEXEC) |
if (!newerror || error == ENOEXEC) |
error = newerror; |
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) |
if (epp->ep_flags & EXEC_DESTR && error != 0) |
return error; |
return error; |
} |
} |
|
|
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); |
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); |
VOP_CLOSE(vp, FREAD, p->p_ucred, p); |
VOP_CLOSE(vp, FREAD, p->p_ucred, p); |
vput(vp); |
vput(vp); |
FREE(ndp->ni_cnd.cn_pnbuf, M_NAMEI); |
PNBUF_PUT(ndp->ni_cnd.cn_pnbuf); |
return error; |
return error; |
|
|
bad1: |
bad1: |
|
|
* (which we don't yet have open). |
* (which we don't yet have open). |
*/ |
*/ |
vput(vp); /* was still locked */ |
vput(vp); /* was still locked */ |
FREE(ndp->ni_cnd.cn_pnbuf, M_NAMEI); |
PNBUF_PUT(ndp->ni_cnd.cn_pnbuf); |
return error; |
return error; |
} |
} |
|
|
|
|
*/ |
*/ |
/* ARGSUSED */ |
/* ARGSUSED */ |
int |
int |
sys_execve(p, v, retval) |
sys_execve(struct proc *p, void *v, register_t *retval) |
register struct proc *p; |
|
void *v; |
|
register_t *retval; |
|
{ |
{ |
register struct sys_execve_args /* { |
struct sys_execve_args /* { |
syscallarg(const char *) path; |
syscallarg(const char *) path; |
syscallarg(char * const *) argp; |
syscallarg(char * const *) argp; |
syscallarg(char * const *) envp; |
syscallarg(char * const *) envp; |
Line 229 sys_execve(p, v, retval) |
|
Line 318 sys_execve(p, v, retval) |
|
struct vmspace *vm; |
struct vmspace *vm; |
char **tmpfap; |
char **tmpfap; |
int szsigcode; |
int szsigcode; |
extern struct emul emul_netbsd; |
struct exec_vmcmd *base_vcp = NULL; |
|
|
/* |
/* |
* figure out the maximum size of an exec header, if necessary. |
* Init the namei data to point the file user's program name. |
* XXX should be able to keep LKM code from modifying exec switch |
* This is done here rather than in check_exec(), so that it's |
* when we're still using it, but... |
* 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); |
NDINIT(&nid, LOOKUP, NOFOLLOW, UIO_USERSPACE, SCARG(uap, path), p); |
|
|
/* |
/* |
* initialize the fields of the exec package. |
* initialize the fields of the exec package. |
*/ |
*/ |
pack.ep_name = SCARG(uap, path); |
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_hdrlen = exec_maxhdrsz; |
pack.ep_hdrvalid = 0; |
pack.ep_hdrvalid = 0; |
pack.ep_ndp = &nid; |
pack.ep_ndp = &nid; |
Line 259 sys_execve(p, v, retval) |
|
Line 341 sys_execve(p, v, retval) |
|
pack.ep_vmcmds.evs_cnt = 0; |
pack.ep_vmcmds.evs_cnt = 0; |
pack.ep_vmcmds.evs_used = 0; |
pack.ep_vmcmds.evs_used = 0; |
pack.ep_vap = &attr; |
pack.ep_vap = &attr; |
pack.ep_emul = &emul_netbsd; |
|
pack.ep_flags = 0; |
pack.ep_flags = 0; |
|
|
|
lockmgr(&exec_lock, LK_SHARED, NULL); |
|
|
/* see if we can run it. */ |
/* see if we can run it. */ |
if ((error = check_exec(p, &pack)) != 0) |
if ((error = check_exec(p, &pack)) != 0) |
goto freehdr; |
goto freehdr; |
Line 342 sys_execve(p, v, retval) |
|
Line 425 sys_execve(p, v, retval) |
|
|
|
dp = (char *) ALIGN(dp); |
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 */ |
/* Now check if args & environ fit into new stack */ |
len = ((argc + envc + 2 + pack.ep_emul->e_arglen) * sizeof(char *) + |
if (pack.ep_flags & EXEC_32) |
sizeof(long) + dp + STACKGAPLEN + szsigcode + |
len = ((argc + envc + 2 + pack.ep_es->es_arglen) * |
sizeof(struct ps_strings)) - argp; |
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 */ |
len = ALIGN(len); /* make the stack "safely" aligned */ |
|
|
Line 374 sys_execve(p, v, retval) |
|
Line 463 sys_execve(p, v, retval) |
|
vm->vm_dsize = btoc(pack.ep_dsize); |
vm->vm_dsize = btoc(pack.ep_dsize); |
vm->vm_ssize = btoc(pack.ep_ssize); |
vm->vm_ssize = btoc(pack.ep_ssize); |
vm->vm_maxsaddr = (char *) pack.ep_maxsaddr; |
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 */ |
/* create the new process's VM space by running the vmcmds */ |
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
Line 384 sys_execve(p, v, retval) |
|
Line 474 sys_execve(p, v, retval) |
|
struct exec_vmcmd *vcp; |
struct exec_vmcmd *vcp; |
|
|
vcp = &pack.ep_vmcmds.evs_cmds[i]; |
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); |
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 */ |
/* free the vmspace-creation commands, and release their references */ |
kill_vmcmds(&pack.ep_vmcmds); |
kill_vmcmds(&pack.ep_vmcmds); |
|
|
/* if an error happened, deallocate and punt */ |
/* 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; |
goto exec_abort; |
|
} |
|
|
/* remember information about the process */ |
/* remember information about the process */ |
arginfo.ps_nargvstr = argc; |
arginfo.ps_nargvstr = argc; |
arginfo.ps_nenvstr = envc; |
arginfo.ps_nenvstr = envc; |
|
|
stack = (char *) (USRSTACK - len); |
stack = (char *) (vm->vm_minsaddr - len); |
/* Now copy argc, args & environ to new stack */ |
/* 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; |
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 */ |
/* 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; |
goto exec_abort; |
|
} |
|
|
/* copy out the process's signal trapoline code */ |
/* copy out the process's signal trapoline code */ |
if (szsigcode) { |
if (szsigcode) { |
if (copyout((char *)pack.ep_emul->e_sigcode, |
if (copyout((char *)pack.ep_es->es_emul->e_sigcode, |
p->p_sigacts->ps_sigcode = (char *)PS_STRINGS - szsigcode, |
p->p_sigacts->ps_sigcode = (char *)p->p_psstr - szsigcode, |
szsigcode)) |
szsigcode)) { |
|
#ifdef DEBUG |
|
printf("execve: sig trampoline copyout failed\n"); |
|
#endif |
goto exec_abort; |
goto exec_abort; |
|
} |
|
#ifdef PMAP_NEED_PROCWR |
|
/* This is code. Let the pmap do what is needed. */ |
|
pmap_procwr(p, (vaddr_t)p->p_sigacts->ps_sigcode, szsigcode); |
|
#endif |
} |
} |
|
|
stopprofclock(p); /* stop profiling */ |
stopprofclock(p); /* stop profiling */ |
Line 463 sys_execve(p, v, retval) |
|
Line 602 sys_execve(p, v, retval) |
|
p->p_cred->p_svuid = p->p_ucred->cr_uid; |
p->p_cred->p_svuid = p->p_ucred->cr_uid; |
p->p_cred->p_svgid = p->p_ucred->cr_gid; |
p->p_cred->p_svgid = p->p_ucred->cr_gid; |
|
|
|
doexechooks(p); |
|
|
uvm_km_free_wakeup(exec_map, (vaddr_t) argp, NCARGS); |
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); |
vn_lock(pack.ep_vp, LK_EXCLUSIVE | LK_RETRY); |
VOP_CLOSE(pack.ep_vp, FREAD, cred, p); |
VOP_CLOSE(pack.ep_vp, FREAD, cred, p); |
vput(pack.ep_vp); |
vput(pack.ep_vp); |
|
|
/* setup new registers and do misc. setup. */ |
/* 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) |
if (p->p_flag & P_TRACED) |
psignal(p, SIGTRAP); |
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 |
#ifdef KTRACE |
if (KTRPOINT(p, KTR_EMUL)) |
if (KTRPOINT(p, KTR_EMUL)) |
ktremul(p->p_tracep, p, p->p_emul->e_name); |
ktremul(p); |
#endif |
#endif |
|
|
|
lockmgr(&exec_lock, LK_RELEASE, NULL); |
|
|
return (EJUSTRETURN); |
return (EJUSTRETURN); |
|
|
bad: |
bad: |
|
|
vn_lock(pack.ep_vp, LK_EXCLUSIVE | LK_RETRY); |
vn_lock(pack.ep_vp, LK_EXCLUSIVE | LK_RETRY); |
VOP_CLOSE(pack.ep_vp, FREAD, cred, p); |
VOP_CLOSE(pack.ep_vp, FREAD, cred, p); |
vput(pack.ep_vp); |
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); |
uvm_km_free_wakeup(exec_map, (vaddr_t) argp, NCARGS); |
|
|
freehdr: |
freehdr: |
FREE(pack.ep_hdr, M_EXEC); |
lockmgr(&exec_lock, LK_RELEASE, NULL); |
|
|
|
free(pack.ep_hdr, M_EXEC); |
return error; |
return error; |
|
|
exec_abort: |
exec_abort: |
|
lockmgr(&exec_lock, LK_RELEASE, NULL); |
|
|
/* |
/* |
* the old process doesn't exist anymore. exit gracefully. |
* the old process doesn't exist anymore. exit gracefully. |
* get rid of the (new) address space we have created, if any, get rid |
* get rid of the (new) address space we have created, if any, get rid |
|
|
VM_MAXUSER_ADDRESS - VM_MIN_ADDRESS); |
VM_MAXUSER_ADDRESS - VM_MIN_ADDRESS); |
if (pack.ep_emul_arg) |
if (pack.ep_emul_arg) |
FREE(pack.ep_emul_arg, M_TEMP); |
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); |
vn_lock(pack.ep_vp, LK_EXCLUSIVE | LK_RETRY); |
VOP_CLOSE(pack.ep_vp, FREAD, cred, p); |
VOP_CLOSE(pack.ep_vp, FREAD, cred, p); |
vput(pack.ep_vp); |
vput(pack.ep_vp); |
uvm_km_free_wakeup(exec_map, (vaddr_t) argp, NCARGS); |
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, W_EXITCODE(0, SIGABRT)); |
exit1(p, -1); |
exit1(p, -1); |
|
|
|
|
|
|
|
|
void * |
void * |
copyargs(pack, arginfo, stack, argp) |
copyargs(struct exec_package *pack, struct ps_strings *arginfo, |
struct exec_package *pack; |
void *stack, void *argp) |
struct ps_strings *arginfo; |
|
void *stack; |
|
void *argp; |
|
{ |
{ |
char **cpp = stack; |
char **cpp = stack; |
char *dp, *sp; |
char *dp, *sp; |
size_t len; |
size_t len; |
void *nullp = NULL; |
void *nullp = NULL; |
int argc = arginfo->ps_nargvstr; |
long argc = arginfo->ps_nargvstr; |
int envc = arginfo->ps_nenvstr; |
long envc = arginfo->ps_nenvstr; |
|
|
|
#ifdef __sparc_v9__ |
|
/* XXX Temporary hack for argc format conversion. */ |
|
argc <<= 32; |
|
#endif |
if (copyout(&argc, cpp++, sizeof(argc))) |
if (copyout(&argc, cpp++, sizeof(argc))) |
return NULL; |
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; |
sp = argp; |
|
|
/* XXX don't copy them out, remap them! */ |
/* XXX don't copy them out, remap them! */ |
Line 572 copyargs(pack, arginfo, stack, argp) |
|
Line 749 copyargs(pack, arginfo, stack, argp) |
|
|
|
return cpp; |
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 */ |