From fc7cfb5f0b78a7b374373b9259e961e4c9346c1f Mon Sep 17 00:00:00 2001 From: fvdl Date: Sun, 11 Jun 1995 14:56:47 +0000 Subject: [PATCH] * Make Linux ELF binaries work. Lots of code duplication, but it will have to do for now. * Add a sanity check in linux_uselib --- sys/compat/linux/arch/i386/syscalls.master | 4 +- sys/compat/linux/common/linux_break.c | 22 +- sys/compat/linux/common/linux_exec.c | 667 +++++++++++++++++- sys/compat/linux/common/linux_exec.h | 9 +- sys/compat/linux/common/linux_exec_aout.c | 667 +++++++++++++++++- sys/compat/linux/common/linux_exec_elf32.c | 667 +++++++++++++++++- sys/compat/linux/common/linux_misc.c | 22 +- sys/compat/linux/common/linux_misc_notalpha.c | 22 +- sys/compat/linux/common/linux_oldmmap.c | 22 +- sys/compat/linux/common/linux_oldolduname.c | 22 +- sys/compat/linux/common/linux_oldselect.c | 22 +- sys/compat/linux/common/linux_olduname.c | 22 +- sys/compat/linux/common/linux_pipe.c | 22 +- sys/compat/linux/i386/syscalls.master | 4 +- sys/compat/linux/include/linux_exec.h | 9 +- sys/compat/linux/linux_exec.c | 667 +++++++++++++++++- sys/compat/linux/linux_exec.h | 9 +- sys/compat/linux/linux_misc.c | 22 +- sys/compat/linux/multiarch/linux_break.c | 22 +- .../linux/multiarch/linux_misc_notalpha.c | 22 +- sys/compat/linux/multiarch/linux_oldmmap.c | 22 +- .../linux/multiarch/linux_oldolduname.c | 22 +- sys/compat/linux/multiarch/linux_oldselect.c | 22 +- sys/compat/linux/multiarch/linux_olduname.c | 22 +- sys/compat/linux/multiarch/linux_pipe.c | 22 +- sys/compat/linux/syscalls.master | 4 +- 26 files changed, 2982 insertions(+), 77 deletions(-) diff --git a/sys/compat/linux/arch/i386/syscalls.master b/sys/compat/linux/arch/i386/syscalls.master index 609123ba74bf..cd4bca11b659 100644 --- a/sys/compat/linux/arch/i386/syscalls.master +++ b/sys/compat/linux/arch/i386/syscalls.master @@ -1,4 +1,4 @@ - $NetBSD: syscalls.master,v 1.5 1995/05/06 18:16:40 mycroft Exp $ + $NetBSD: syscalls.master,v 1.6 1995/06/11 14:57:01 fvdl Exp $ ; @(#)syscalls.master 8.1 (Berkeley) 7/19/93 @@ -194,7 +194,7 @@ 133 NOARGS { int fchdir(int fd); } 134 UNIMPL linux_bdflush 135 UNIMPL linux_sysfs -136 UNIMPL linux_personality +136 STD { int linux_personality(int per); } 137 UNIMPL linux_afs_syscall 138 UNIMPL linux_setfsuid 139 UNIMPL linux_getfsuid diff --git a/sys/compat/linux/common/linux_break.c b/sys/compat/linux/common/linux_break.c index 0bc1348a01b4..8b3d5a746e13 100644 --- a/sys/compat/linux/common/linux_break.c +++ b/sys/compat/linux/common/linux_break.c @@ -1,4 +1,4 @@ -/* $NetBSD: linux_break.c,v 1.5 1995/06/10 22:19:14 mycroft Exp $ */ +/* $NetBSD: linux_break.c,v 1.6 1995/06/11 14:56:59 fvdl Exp $ */ /* * Copyright (c) 1995 Frank van der Linden @@ -789,3 +789,23 @@ linux_getpgid(p, uap, retval) retval[0] = targp->p_pgid; return 0; } + +/* + * Set the 'personality' (emulation mode) for the current process. Only + * accept the Linux personality here (0). This call is needed because + * the Linux ELF crt0 issues it in an ugly kludge to make sure that + * ELF binaries run in Linux mode, not SVR4 mode. + */ +int +linux_personality(p, uap, retval) + struct proc *p; + struct linux_personality_args /* P + syscallarg(int) per; + } */ *uap; + register_t *retval; +{ + if (SCARG(uap, per) != 0) + return EINVAL; + retval[0] = 0; + return 0; +} diff --git a/sys/compat/linux/common/linux_exec.c b/sys/compat/linux/common/linux_exec.c index 99792655576b..64c6b6963a90 100644 --- a/sys/compat/linux/common/linux_exec.c +++ b/sys/compat/linux/common/linux_exec.c @@ -1,4 +1,4 @@ -/* $NetBSD: linux_exec.c,v 1.5 1995/05/16 14:19:07 mycroft Exp $ */ +/* $NetBSD: linux_exec.c,v 1.6 1995/06/11 14:56:47 fvdl Exp $ */ /* * Copyright (c) 1995 Frank van der Linden @@ -64,16 +64,43 @@ #include #include -static void *linux_copyargs __P((struct exec_package *, struct ps_strings *, - void *, void *)); +struct elf_args { + u_long arg_entry; /* progran entry point */ + u_long arg_interp; /* Interpreter load address */ + u_long arg_phaddr; /* program header address */ + u_long arg_phentsize; /* Size of program header */ + u_long arg_phnum; /* Number of program headers */ +}; -#define LINUX_AUX_ARGSIZ 2 +static void *linux_aout_copyargs __P((struct exec_package *, + struct ps_strings *, void *, void *)); +static void *linux_elf_copyargs __P((struct exec_package *, struct ps_strings *, + void *, void *)); +static int linux_elf_check_header __P((Elf32_Ehdr *, int)); +static void linux_elf_load_psection __P((struct exec_vmcmd_set *, + struct vnode *, Elf32_Phdr *, u_long *, u_long *, int *)); +static int linux_elf_set_segment __P((struct exec_package *, u_long, u_long, + int)); +static int linux_elf_read_from __P((struct vnode *, u_long, struct proc *, + caddr_t, int)); +static int linux_elf_load_file __P((struct proc *, char *, + struct exec_vmcmd_set *, u_long *, struct elf_args *, u_long *)); + +#ifdef DEBUG_EXEC_LINUX_ELF +#define DPRINTF(x) printf x +#else +#define DPRINTF(x) +#endif + +#define LINUX_ELF_ALIGN(a, b) ((a) & ~((b) - 1)) +#define LINUX_ELF_AUX_ARGSIZ (sizeof(AuxInfo) * 8 / sizeof(char *)) +#define LINUX_AOUT_AUX_ARGSIZ 2 extern int linux_error[]; extern struct sysent linux_sysent[]; extern char *linux_syscallnames[]; -struct emul emul_linux = { +struct emul emul_linux_aout = { "linux", linux_error, linux_sendsig, @@ -81,8 +108,23 @@ struct emul emul_linux = { LINUX_SYS_MAXSYSCALL, linux_sysent, linux_syscallnames, - LINUX_AUX_ARGSIZ, - linux_copyargs, + LINUX_AOUT_AUX_ARGSIZ, + linux_aout_copyargs, + setregs, + linux_sigcode, + linux_esigcode, +}; + +struct emul emul_linux_elf = { + "linux", + linux_error, + linux_sendsig, + LINUX_SYS_syscall, + LINUX_SYS_MAXSYSCALL, + linux_sysent, + linux_syscallnames, + LINUX_ELF_AUX_ARGSIZ, + linux_elf_copyargs, setregs, linux_sigcode, linux_esigcode, @@ -90,7 +132,7 @@ struct emul emul_linux = { static void * -linux_copyargs(pack, arginfo, stack, argp) +linux_aout_copyargs(pack, arginfo, stack, argp) struct exec_package *pack; struct ps_strings *arginfo; void *stack; @@ -142,6 +184,140 @@ linux_copyargs(pack, arginfo, stack, argp) return cpp; } +static void * +linux_elf_copyargs(pack, arginfo, stack, argp) + 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; + AuxInfo *a; + struct elf_args *ap; + + if (copyout(&argc, cpp++, sizeof(argc))) + return NULL; + + dp = (char *) (cpp + argc + envc + 2 + pack->ep_emul->e_arglen); + sp = argp; + + /* XXX don't copy them out, remap them! */ + arginfo->ps_argvstr = cpp; /* remember location of argv for later */ + + for (; --argc >= 0; sp += len, dp += len) + if (copyout(&dp, cpp++, sizeof(dp)) || + copyoutstr(sp, dp, ARG_MAX, &len)) + return NULL; + + if (copyout(&nullp, cpp++, sizeof(nullp))) + return NULL; + + arginfo->ps_envstr = cpp; /* remember location of envp for later */ + + for (; --envc >= 0; sp += len, dp += len) + if (copyout(&dp, cpp++, sizeof(dp)) || + copyoutstr(sp, dp, ARG_MAX, &len)) + return NULL; + + if (copyout(&nullp, cpp++, sizeof(nullp))) + return NULL; + + /* + * Push extra arguments on the stack needed by dynamically + * linked binaries + */ + a = (AuxInfo *) cpp; + if ((ap = (struct elf_args *) pack->ep_emul_arg)) { + + DPRINTF(("phaddr=0x%x, phsize=%d, phnum=%d, interp=0x%x, ", + ap->arg_phaddr, ap->arg_phentsize, ap->arg_phnum, + ap->arg_interp)); + DPRINTF((" entry=0x%x\n", ap->arg_entry)); + + a->au_id = AUX_phdr; + a->au_v = ap->arg_phaddr; + a++; + + a->au_id = AUX_phent; + a->au_v = ap->arg_phentsize; + a++; + + a->au_id = AUX_phnum; + a->au_v = ap->arg_phnum; + a++; + + a->au_id = AUX_pagesz; + a->au_v = NBPG; + a++; + + a->au_id = AUX_base; + a->au_v = ap->arg_interp; + a++; + + a->au_id = AUX_flags; + a->au_v = 0; + a++; + + a->au_id = AUX_entry; + a->au_v = ap->arg_entry; + a++; + + a->au_id = AUX_null; + a->au_v = 0; + a++; + + free((char *) ap, M_TEMP); + } + return a; +} + +#ifdef DEBUG_EXEC_LINUX_ELF +static void +print_Ehdr(e) + Elf32_Ehdr *e; +{ + printf("e_ident %s, ", e->e_ident); + printf("e_type %d, ", e->e_type); + printf("e_machine %d, ", e->e_machine); + printf("e_version %ld, ", e->e_version); + printf("e_entry %lx, ", e->e_entry); + printf("e_phoff %lx, ", e->e_phoff); + printf("e_shoff %lx, ", e->e_shoff); + printf("e_flags %lx, ", e->e_flags); + printf("e_ehsize %d, ", e->e_ehsize); + printf("e_phentsize %d, ", e->e_phentsize); + printf("e_phnum %d, ", e->e_phnum); + printf("e_shentsize %d, ", e->e_shentsize); + printf("e_shnum %d, ", e->e_shnum); + printf("e_shstrndx %d\n", e->e_shstrndx); +} + + +static void +print_Phdr(p) + Elf32_Phdr *p; +{ + static char *types[] = + { + "null", "load", "dynamic", "interp", + "note", "shlib", "phdr", "entry7" + }; + + printf("p_type %ld [%s], ", p->p_type, types[p->p_type & 7]); + printf("p_offset %lx, ", p->p_offset); + printf("p_vaddr %lx, ", p->p_vaddr); + printf("p_paddr %lx, ", p->p_paddr); + printf("p_filesz %ld, ", p->p_filesz); + printf("p_memsz %ld, ", p->p_memsz); + printf("p_flags %lx, ", p->p_flags); + printf("p_align %ld\n", p->p_align); +} +#endif int exec_linux_aout_makecmds(p, epp) @@ -174,7 +350,7 @@ exec_linux_aout_makecmds(p, epp) break; } if (error == 0) - epp->ep_emul = &emul_linux; + epp->ep_emul = &emul_linux_aout; return error; } @@ -345,9 +521,471 @@ exec_linux_aout_prep_qmagic(p, epp) } /* - * The Linux system call to load shared libraries. The current shared - * libraries are just (QMAGIC) a.out files that are mapped onto a fixed - * address * in the process' address space. The address is given in + * linux_elf_check_header(): + * + * Check header for validity; return 0 of ok ENOEXEC if error + */ +static int +linux_elf_check_header(eh, type) + Elf32_Ehdr *eh; + int type; +{ +#ifdef sparc + /* #$%@#$%@#$%! */ +# define memcmp bcmp +#endif + if (memcmp(eh->e_ident, Elf32_e_ident, Elf32_e_siz) != 0) { + DPRINTF(("Not an elf file\n")); + return ENOEXEC; + } + + switch (eh->e_machine) { +#ifdef i386 + case Elf32_em_386: + case Elf32_em_486: +#endif +#ifdef sparc + case Elf32_em_sparc: +#endif + break; + + default: + DPRINTF(("Unsupported elf machine type %d\n", eh->e_machine)); + return ENOEXEC; + } + + if (eh->e_type != type) { + DPRINTF(("Not an elf executable\n")); + return ENOEXEC; + } + + return 0; +} + + +/* + * linux_elf_load_psection(): + * + * Load a psection at the appropriate address + */ +static void +linux_elf_load_psection(vcset, vp, ph, addr, size, prot) + struct exec_vmcmd_set *vcset; + struct vnode *vp; + Elf32_Phdr *ph; + u_long *addr; + u_long *size; + int *prot; +{ + u_long uaddr; + long diff; + long offset; + u_long msize; + + /* + * If the user specified an address, then we load there. + */ + if (*addr != ~0) { + uaddr = *addr + ph->p_align; + *addr = LINUX_ELF_ALIGN(uaddr, ph->p_align); + uaddr = LINUX_ELF_ALIGN(ph->p_vaddr, ph->p_align); + diff = ph->p_vaddr - uaddr; + } else { + uaddr = ph->p_vaddr; + *addr = LINUX_ELF_ALIGN(uaddr, ph->p_align); + diff = uaddr - *addr; + } + + *prot |= (ph->p_flags & Elf32_pf_r) ? VM_PROT_READ : 0; + *prot |= (ph->p_flags & Elf32_pf_w) ? VM_PROT_WRITE : 0; + *prot |= (ph->p_flags & Elf32_pf_x) ? VM_PROT_EXECUTE : 0; + + offset = ph->p_offset - diff; + *size = ph->p_filesz + diff; + msize = ph->p_memsz + diff; + + DPRINTF(("Elf Seg@ 0x%x/0x%x sz %d/%d off 0x%x/0x%x[%d] algn 0x%x\n", + ph->p_vaddr, *addr, *size, msize, ph->p_offset, offset, + diff, ph->p_align)); + + NEW_VMCMD(vcset, vmcmd_map_readvn, *size, + *addr, vp, offset, *prot); + + /* + * Check if we need to extend the size of the segment + */ + { + u_long rm = round_page(*addr + msize); + u_long rf = round_page(*addr + *size); + if (rm != rf) { + DPRINTF(("zeropad 0x%x-0x%x\n", rf, rm)); + NEW_VMCMD(vcset, vmcmd_map_zero, rm - rf, + rf, NULLVP, 0, *prot); + *size = msize; + } + } +} + + +/* + * linux_elf_set_segment(): + * + * Decide if the segment is text or data, depending on the protection + * and set it appropriately + */ +static int +linux_elf_set_segment(epp, vaddr, size, prot) + struct exec_package *epp; + u_long vaddr; + u_long size; + int prot; +{ + /* + * Kludge: Unfortunately the current implementation of + * exec package assumes a single text and data segment. + * In Elf we can have more, but here we limit ourselves + * to two and hope :-( + * We also assume that the text is r-x, and data is rwx. + */ + switch (prot) { + case (VM_PROT_READ | VM_PROT_EXECUTE): + if (epp->ep_tsize != ~0) { + DPRINTF(("More than one text segment\n")); + return ENOEXEC; + } + epp->ep_taddr = vaddr; + epp->ep_tsize = size; + DPRINTF(("Elf Text@ 0x%x, size %d\n", vaddr, size)); + break; + + case (VM_PROT_READ | VM_PROT_WRITE): + case (VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE): + if (epp->ep_dsize != ~0) { + DPRINTF(("More than one data segment\n")); + return ENOEXEC; + } + epp->ep_daddr = vaddr; + epp->ep_dsize = size; + + DPRINTF(("Elf Data@ 0x%x, size %d\n", vaddr, size)); + break; + + default: + DPRINTF(("Bad protection 0%o\n", prot)); + return ENOEXEC; + } + return 0; +} + + +/* + * linux_elf_read_from(): + * + * Read from vnode into buffer at offset. + */ +static int +linux_elf_read_from(vp, off, p, buf, size) + struct vnode *vp; + u_long off; + struct proc *p; + caddr_t buf; + int size; +{ + int error; + int resid; + + DPRINTF(("read from 0x%x to 0x%x size %d\n", + off, buf, size)); + if ((error = vn_rdwr(UIO_READ, vp, buf, size, + off, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, + &resid, p)) != 0) { + DPRINTF(("Bad read error %d\n", error)); + return error; + } + /* + * See if we got all of it + */ + if (resid != 0) { + DPRINTF(("Incomplete read for header ask=%d, rem=%d\n", + size, resid)); + return error; + } + return 0; +} + + +/* + * linux_elf_load_file(): + * + * Load a file (interpreter/library) pointed to by path + * [stolen from coff_load_shlib()]. Made slightly more generic than + * the svr4 version, for possible later use in linux_uselib(). + */ +static int +linux_elf_load_file(p, path, vcset, entry, ap, last) + struct proc *p; + char *path; + struct exec_vmcmd_set *vcset; + u_long *entry; + struct elf_args *ap; + u_long *last; +{ + int error, i; + struct nameidata nd; + Elf32_Ehdr eh; + Elf32_Phdr *ph = NULL; + u_long phsize; + char *bp = NULL; + u_long addr = *last; + + DPRINTF(("Loading file %s @ %x\n", path, addr)); + + if ((error = linux_emul_find(p, NULL, linux_emul_path, path, &bp, 0)) != 0) + bp = NULL; + else + path = bp; + /* + * 1. open file + * 2. read filehdr + * 3. map text, data, and bss out of it using VM_* + */ + NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, p); + /* first get the vnode */ + if ((error = namei(&nd)) != 0) { + if (bp != NULL) + free((char *) bp, M_TEMP); + return error; + } + if ((error = linux_elf_read_from(nd.ni_vp, 0, p, (caddr_t) &eh, + sizeof(eh))) != 0) + goto bad; + +#ifdef DEBUG_EXEC_LINUX_ELF + print_Ehdr(&eh); +#endif + + if ((error = linux_elf_check_header(&eh, Elf32_et_dyn)) != 0) + goto bad; + + phsize = eh.e_phnum * sizeof(Elf32_Phdr); + ph = (Elf32_Phdr *) malloc(phsize, M_TEMP, M_WAITOK); + + if ((error = linux_elf_read_from(nd.ni_vp, eh.e_phoff, p, + (caddr_t) ph, phsize)) != 0) + goto bad; + + /* + * Load all the necessary sections + */ + for (i = 0; i < eh.e_phnum; i++) { + u_long size = 0; + int prot = 0; +#ifdef DEBUG_EXEC_LINUX_ELF + print_Phdr(&ph[i]); +#endif + + switch (ph[i].p_type) { + case Elf32_pt_load: + linux_elf_load_psection(vcset, nd.ni_vp, &ph[i], &addr, + &size, &prot); + /* Assume that the text segment is r-x only */ + if ((prot & PROT_WRITE) == 0) { + *entry = addr + eh.e_entry; + ap->arg_interp = addr; + DPRINTF(("Interpreter@ 0x%x\n", addr)); + } + addr += size; + break; + + case Elf32_pt_dynamic: + case Elf32_pt_phdr: + case Elf32_pt_note: + break; + + default: + DPRINTF(("interp: Unexpected program header type %d\n", + ph[i].p_type)); + break; + } + } + +bad: + if (ph != NULL) + free((char *) ph, M_TEMP); + if (bp != NULL) + free((char *) bp, M_TEMP); + + *last = addr; + vrele(nd.ni_vp); + return error; +} + + +/* + * exec_linux_elf_makecmds(): Prepare an Elf binary's exec package + * + * First, set of the various offsets/lengths in the exec package. + * + * Then, mark the text image busy (so it can be demand paged) or error + * out if this is not possible. Finally, set up vmcmds for the + * text, data, bss, and stack segments. + */ +int +exec_linux_elf_makecmds(p, epp) + struct proc *p; + struct exec_package *epp; +{ + Elf32_Ehdr *eh = epp->ep_hdr; + Elf32_Phdr *ph, *pp; + int error; + int i; + char interp[MAXPATHLEN]; + u_long pos = 0; + u_long phsize; + +#ifdef DEBUG_EXEC_LINUX_ELF + print_Ehdr(eh); +#endif + if (epp->ep_hdrvalid < sizeof(Elf32_Ehdr)) + return ENOEXEC; + + if (linux_elf_check_header(eh, Elf32_et_exec)) + return ENOEXEC; + + /* + * check if vnode is in open for writing, because we want to + * demand-page out of it. if it is, don't do it, for various + * reasons + */ + if (epp->ep_vp->v_writecount != 0) { +#ifdef DIAGNOSTIC + if (epp->ep_vp->v_flag & VTEXT) + panic("exec: a VTEXT vnode has writecount != 0\n"); +#endif + return ETXTBSY; + } + /* + * Allocate space to hold all the program headers, and read them + * from the file + */ + phsize = eh->e_phnum * sizeof(Elf32_Phdr); + ph = (Elf32_Phdr *) malloc(phsize, M_TEMP, M_WAITOK); + + if ((error = linux_elf_read_from(epp->ep_vp, eh->e_phoff, p, + (caddr_t) ph, phsize)) != 0) + goto bad; + + epp->ep_tsize = ~0; + epp->ep_dsize = ~0; + + interp[0] = '\0'; + + /* + * Load all the necessary sections + */ + for (i = 0; i < eh->e_phnum; i++) { + u_long addr = ~0, size = 0; + int prot = 0; + + pp = &ph[i]; +#ifdef DEBUG_EXEC_LINUX_ELF + print_Phdr(pp); +#endif + + switch (ph[i].p_type) { + case Elf32_pt_load: + linux_elf_load_psection(&epp->ep_vmcmds, epp->ep_vp, + &ph[i], &addr, &size, &prot); + if ((error = linux_elf_set_segment(epp, addr, size, + prot)) != 0) + goto bad; + break; + + case Elf32_pt_shlib: + DPRINTF(("No support for COFF libraries (yet)\n")); + error = ENOEXEC; + goto bad; + + case Elf32_pt_interp: + if (pp->p_filesz >= sizeof(interp)) { + DPRINTF(("Interpreter path too long %d\n", + pp->p_filesz)); + goto bad; + } + if ((error = linux_elf_read_from(epp->ep_vp, pp->p_offset, p, + (caddr_t) interp, pp->p_filesz)) != 0) + goto bad; + break; + + case Elf32_pt_dynamic: + case Elf32_pt_phdr: + case Elf32_pt_note: + break; + + default: + /* + * Not fatal, we don't need to understand everything + * :-) + */ + DPRINTF(("Unsupported program header type %d\n", + pp->p_type)); + break; + } + } + + /* + * Check if we found a dynamically linked binary and arrange to load + * it's interpreter + */ + if (interp[0]) { + struct elf_args *ap; + pos = ~0; + + ap = (struct elf_args *) malloc(sizeof(struct elf_args), + M_TEMP, M_WAITOK); + if ((error = linux_elf_load_file(p, interp, &epp->ep_vmcmds, + &epp->ep_entry, ap, &pos)) != 0) { + free((char *) ap, M_TEMP); + goto bad; + } + /* Arrange to load the program headers. */ + pos = LINUX_ELF_ALIGN(pos + NBPG, NBPG); + DPRINTF(("Program header @0x%x\n", pos)); + ap->arg_phaddr = pos; + NEW_VMCMD(&epp->ep_vmcmds, vmcmd_map_readvn, phsize, + pos, epp->ep_vp, eh->e_phoff, + VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE); + pos += phsize; + + ap->arg_phentsize = eh->e_phentsize; + ap->arg_phnum = eh->e_phnum; + ap->arg_entry = eh->e_entry; + + epp->ep_emul_arg = ap; + } else + epp->ep_entry = eh->e_entry; + + DPRINTF(("taddr 0x%x tsize 0x%x daddr 0x%x dsize 0x%x\n", + epp->ep_taddr, epp->ep_tsize, epp->ep_daddr, epp->ep_dsize)); + + free((char *) ph, M_TEMP); + + DPRINTF(("Elf entry@ 0x%x\n", epp->ep_entry)); + epp->ep_vp->v_flag |= VTEXT; + + epp->ep_emul = &emul_linux_elf; + + return exec_aout_setup_stack(p, epp); + +bad: + free((char *) ph, M_TEMP); + kill_vmcmds(&epp->ep_vmcmds); + return ENOEXEC; +} +/* + * The Linux system call to load shared libraries, a.out version. The + * a.out shared libs are just files that are mapped onto a fixed + * address in the process' address space. The address is given in * a_entry. Read in the header, set up some VM commands and run them. * * Yes, both text and data are mapped at once, so we're left with @@ -357,7 +995,7 @@ exec_linux_aout_prep_qmagic(p, epp) * Yuck. * * Because of the problem with ZMAGIC executables (text starts - * at 0x400 in the file, but needs t be mapped at 0), ZMAGIC + * at 0x400 in the file, but needs to be mapped at 0), ZMAGIC * shared libs are not handled very efficiently :-( */ @@ -399,6 +1037,9 @@ linux_uselib(p, uap, retval) return ENOEXEC; } + if (LINUX_N_MACHTYPE(&hdr) != LINUX_MID_MACHINE) + return ENOEXEC; + magic = LINUX_N_MAGIC(&hdr); taddr = hdr.a_entry & (~(NBPG - 1)); tsize = hdr.a_text; diff --git a/sys/compat/linux/common/linux_exec.h b/sys/compat/linux/common/linux_exec.h index 4bd97aeb20cd..4645b901a74c 100644 --- a/sys/compat/linux/common/linux_exec.h +++ b/sys/compat/linux/common/linux_exec.h @@ -1,4 +1,4 @@ -/* $NetBSD: linux_exec.h,v 1.2 1995/04/07 22:23:26 fvdl Exp $ */ +/* $NetBSD: linux_exec.h,v 1.3 1995/06/11 14:56:56 fvdl Exp $ */ /* * Copyright (c) 1995 Frank van der Linden @@ -35,6 +35,12 @@ #ifndef _LINUX_EXEC_H #define _LINUX_EXEC_H +/* + * Include this for ELF definitions. Should be an emul-independent file + * someday. + */ +#include + #define LINUX_M_I386 100 /* Sparc? Alpha? */ @@ -66,6 +72,7 @@ #define LINUX_N_BSSADDR(x,m) (LINUX_N_DATADDR(x,m) + (x).a_data) int exec_linux_aout_makecmds __P((struct proc *, struct exec_package *)); +int exec_linux_elf_makecmds __P((struct proc *, struct exec_package *)); extern char linux_sigcode[], linux_esigcode[]; diff --git a/sys/compat/linux/common/linux_exec_aout.c b/sys/compat/linux/common/linux_exec_aout.c index 510d0f2dfdce..61bf36872ce1 100644 --- a/sys/compat/linux/common/linux_exec_aout.c +++ b/sys/compat/linux/common/linux_exec_aout.c @@ -1,4 +1,4 @@ -/* $NetBSD: linux_exec_aout.c,v 1.5 1995/05/16 14:19:07 mycroft Exp $ */ +/* $NetBSD: linux_exec_aout.c,v 1.6 1995/06/11 14:56:47 fvdl Exp $ */ /* * Copyright (c) 1995 Frank van der Linden @@ -64,16 +64,43 @@ #include #include -static void *linux_copyargs __P((struct exec_package *, struct ps_strings *, - void *, void *)); +struct elf_args { + u_long arg_entry; /* progran entry point */ + u_long arg_interp; /* Interpreter load address */ + u_long arg_phaddr; /* program header address */ + u_long arg_phentsize; /* Size of program header */ + u_long arg_phnum; /* Number of program headers */ +}; -#define LINUX_AUX_ARGSIZ 2 +static void *linux_aout_copyargs __P((struct exec_package *, + struct ps_strings *, void *, void *)); +static void *linux_elf_copyargs __P((struct exec_package *, struct ps_strings *, + void *, void *)); +static int linux_elf_check_header __P((Elf32_Ehdr *, int)); +static void linux_elf_load_psection __P((struct exec_vmcmd_set *, + struct vnode *, Elf32_Phdr *, u_long *, u_long *, int *)); +static int linux_elf_set_segment __P((struct exec_package *, u_long, u_long, + int)); +static int linux_elf_read_from __P((struct vnode *, u_long, struct proc *, + caddr_t, int)); +static int linux_elf_load_file __P((struct proc *, char *, + struct exec_vmcmd_set *, u_long *, struct elf_args *, u_long *)); + +#ifdef DEBUG_EXEC_LINUX_ELF +#define DPRINTF(x) printf x +#else +#define DPRINTF(x) +#endif + +#define LINUX_ELF_ALIGN(a, b) ((a) & ~((b) - 1)) +#define LINUX_ELF_AUX_ARGSIZ (sizeof(AuxInfo) * 8 / sizeof(char *)) +#define LINUX_AOUT_AUX_ARGSIZ 2 extern int linux_error[]; extern struct sysent linux_sysent[]; extern char *linux_syscallnames[]; -struct emul emul_linux = { +struct emul emul_linux_aout = { "linux", linux_error, linux_sendsig, @@ -81,8 +108,23 @@ struct emul emul_linux = { LINUX_SYS_MAXSYSCALL, linux_sysent, linux_syscallnames, - LINUX_AUX_ARGSIZ, - linux_copyargs, + LINUX_AOUT_AUX_ARGSIZ, + linux_aout_copyargs, + setregs, + linux_sigcode, + linux_esigcode, +}; + +struct emul emul_linux_elf = { + "linux", + linux_error, + linux_sendsig, + LINUX_SYS_syscall, + LINUX_SYS_MAXSYSCALL, + linux_sysent, + linux_syscallnames, + LINUX_ELF_AUX_ARGSIZ, + linux_elf_copyargs, setregs, linux_sigcode, linux_esigcode, @@ -90,7 +132,7 @@ struct emul emul_linux = { static void * -linux_copyargs(pack, arginfo, stack, argp) +linux_aout_copyargs(pack, arginfo, stack, argp) struct exec_package *pack; struct ps_strings *arginfo; void *stack; @@ -142,6 +184,140 @@ linux_copyargs(pack, arginfo, stack, argp) return cpp; } +static void * +linux_elf_copyargs(pack, arginfo, stack, argp) + 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; + AuxInfo *a; + struct elf_args *ap; + + if (copyout(&argc, cpp++, sizeof(argc))) + return NULL; + + dp = (char *) (cpp + argc + envc + 2 + pack->ep_emul->e_arglen); + sp = argp; + + /* XXX don't copy them out, remap them! */ + arginfo->ps_argvstr = cpp; /* remember location of argv for later */ + + for (; --argc >= 0; sp += len, dp += len) + if (copyout(&dp, cpp++, sizeof(dp)) || + copyoutstr(sp, dp, ARG_MAX, &len)) + return NULL; + + if (copyout(&nullp, cpp++, sizeof(nullp))) + return NULL; + + arginfo->ps_envstr = cpp; /* remember location of envp for later */ + + for (; --envc >= 0; sp += len, dp += len) + if (copyout(&dp, cpp++, sizeof(dp)) || + copyoutstr(sp, dp, ARG_MAX, &len)) + return NULL; + + if (copyout(&nullp, cpp++, sizeof(nullp))) + return NULL; + + /* + * Push extra arguments on the stack needed by dynamically + * linked binaries + */ + a = (AuxInfo *) cpp; + if ((ap = (struct elf_args *) pack->ep_emul_arg)) { + + DPRINTF(("phaddr=0x%x, phsize=%d, phnum=%d, interp=0x%x, ", + ap->arg_phaddr, ap->arg_phentsize, ap->arg_phnum, + ap->arg_interp)); + DPRINTF((" entry=0x%x\n", ap->arg_entry)); + + a->au_id = AUX_phdr; + a->au_v = ap->arg_phaddr; + a++; + + a->au_id = AUX_phent; + a->au_v = ap->arg_phentsize; + a++; + + a->au_id = AUX_phnum; + a->au_v = ap->arg_phnum; + a++; + + a->au_id = AUX_pagesz; + a->au_v = NBPG; + a++; + + a->au_id = AUX_base; + a->au_v = ap->arg_interp; + a++; + + a->au_id = AUX_flags; + a->au_v = 0; + a++; + + a->au_id = AUX_entry; + a->au_v = ap->arg_entry; + a++; + + a->au_id = AUX_null; + a->au_v = 0; + a++; + + free((char *) ap, M_TEMP); + } + return a; +} + +#ifdef DEBUG_EXEC_LINUX_ELF +static void +print_Ehdr(e) + Elf32_Ehdr *e; +{ + printf("e_ident %s, ", e->e_ident); + printf("e_type %d, ", e->e_type); + printf("e_machine %d, ", e->e_machine); + printf("e_version %ld, ", e->e_version); + printf("e_entry %lx, ", e->e_entry); + printf("e_phoff %lx, ", e->e_phoff); + printf("e_shoff %lx, ", e->e_shoff); + printf("e_flags %lx, ", e->e_flags); + printf("e_ehsize %d, ", e->e_ehsize); + printf("e_phentsize %d, ", e->e_phentsize); + printf("e_phnum %d, ", e->e_phnum); + printf("e_shentsize %d, ", e->e_shentsize); + printf("e_shnum %d, ", e->e_shnum); + printf("e_shstrndx %d\n", e->e_shstrndx); +} + + +static void +print_Phdr(p) + Elf32_Phdr *p; +{ + static char *types[] = + { + "null", "load", "dynamic", "interp", + "note", "shlib", "phdr", "entry7" + }; + + printf("p_type %ld [%s], ", p->p_type, types[p->p_type & 7]); + printf("p_offset %lx, ", p->p_offset); + printf("p_vaddr %lx, ", p->p_vaddr); + printf("p_paddr %lx, ", p->p_paddr); + printf("p_filesz %ld, ", p->p_filesz); + printf("p_memsz %ld, ", p->p_memsz); + printf("p_flags %lx, ", p->p_flags); + printf("p_align %ld\n", p->p_align); +} +#endif int exec_linux_aout_makecmds(p, epp) @@ -174,7 +350,7 @@ exec_linux_aout_makecmds(p, epp) break; } if (error == 0) - epp->ep_emul = &emul_linux; + epp->ep_emul = &emul_linux_aout; return error; } @@ -345,9 +521,471 @@ exec_linux_aout_prep_qmagic(p, epp) } /* - * The Linux system call to load shared libraries. The current shared - * libraries are just (QMAGIC) a.out files that are mapped onto a fixed - * address * in the process' address space. The address is given in + * linux_elf_check_header(): + * + * Check header for validity; return 0 of ok ENOEXEC if error + */ +static int +linux_elf_check_header(eh, type) + Elf32_Ehdr *eh; + int type; +{ +#ifdef sparc + /* #$%@#$%@#$%! */ +# define memcmp bcmp +#endif + if (memcmp(eh->e_ident, Elf32_e_ident, Elf32_e_siz) != 0) { + DPRINTF(("Not an elf file\n")); + return ENOEXEC; + } + + switch (eh->e_machine) { +#ifdef i386 + case Elf32_em_386: + case Elf32_em_486: +#endif +#ifdef sparc + case Elf32_em_sparc: +#endif + break; + + default: + DPRINTF(("Unsupported elf machine type %d\n", eh->e_machine)); + return ENOEXEC; + } + + if (eh->e_type != type) { + DPRINTF(("Not an elf executable\n")); + return ENOEXEC; + } + + return 0; +} + + +/* + * linux_elf_load_psection(): + * + * Load a psection at the appropriate address + */ +static void +linux_elf_load_psection(vcset, vp, ph, addr, size, prot) + struct exec_vmcmd_set *vcset; + struct vnode *vp; + Elf32_Phdr *ph; + u_long *addr; + u_long *size; + int *prot; +{ + u_long uaddr; + long diff; + long offset; + u_long msize; + + /* + * If the user specified an address, then we load there. + */ + if (*addr != ~0) { + uaddr = *addr + ph->p_align; + *addr = LINUX_ELF_ALIGN(uaddr, ph->p_align); + uaddr = LINUX_ELF_ALIGN(ph->p_vaddr, ph->p_align); + diff = ph->p_vaddr - uaddr; + } else { + uaddr = ph->p_vaddr; + *addr = LINUX_ELF_ALIGN(uaddr, ph->p_align); + diff = uaddr - *addr; + } + + *prot |= (ph->p_flags & Elf32_pf_r) ? VM_PROT_READ : 0; + *prot |= (ph->p_flags & Elf32_pf_w) ? VM_PROT_WRITE : 0; + *prot |= (ph->p_flags & Elf32_pf_x) ? VM_PROT_EXECUTE : 0; + + offset = ph->p_offset - diff; + *size = ph->p_filesz + diff; + msize = ph->p_memsz + diff; + + DPRINTF(("Elf Seg@ 0x%x/0x%x sz %d/%d off 0x%x/0x%x[%d] algn 0x%x\n", + ph->p_vaddr, *addr, *size, msize, ph->p_offset, offset, + diff, ph->p_align)); + + NEW_VMCMD(vcset, vmcmd_map_readvn, *size, + *addr, vp, offset, *prot); + + /* + * Check if we need to extend the size of the segment + */ + { + u_long rm = round_page(*addr + msize); + u_long rf = round_page(*addr + *size); + if (rm != rf) { + DPRINTF(("zeropad 0x%x-0x%x\n", rf, rm)); + NEW_VMCMD(vcset, vmcmd_map_zero, rm - rf, + rf, NULLVP, 0, *prot); + *size = msize; + } + } +} + + +/* + * linux_elf_set_segment(): + * + * Decide if the segment is text or data, depending on the protection + * and set it appropriately + */ +static int +linux_elf_set_segment(epp, vaddr, size, prot) + struct exec_package *epp; + u_long vaddr; + u_long size; + int prot; +{ + /* + * Kludge: Unfortunately the current implementation of + * exec package assumes a single text and data segment. + * In Elf we can have more, but here we limit ourselves + * to two and hope :-( + * We also assume that the text is r-x, and data is rwx. + */ + switch (prot) { + case (VM_PROT_READ | VM_PROT_EXECUTE): + if (epp->ep_tsize != ~0) { + DPRINTF(("More than one text segment\n")); + return ENOEXEC; + } + epp->ep_taddr = vaddr; + epp->ep_tsize = size; + DPRINTF(("Elf Text@ 0x%x, size %d\n", vaddr, size)); + break; + + case (VM_PROT_READ | VM_PROT_WRITE): + case (VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE): + if (epp->ep_dsize != ~0) { + DPRINTF(("More than one data segment\n")); + return ENOEXEC; + } + epp->ep_daddr = vaddr; + epp->ep_dsize = size; + + DPRINTF(("Elf Data@ 0x%x, size %d\n", vaddr, size)); + break; + + default: + DPRINTF(("Bad protection 0%o\n", prot)); + return ENOEXEC; + } + return 0; +} + + +/* + * linux_elf_read_from(): + * + * Read from vnode into buffer at offset. + */ +static int +linux_elf_read_from(vp, off, p, buf, size) + struct vnode *vp; + u_long off; + struct proc *p; + caddr_t buf; + int size; +{ + int error; + int resid; + + DPRINTF(("read from 0x%x to 0x%x size %d\n", + off, buf, size)); + if ((error = vn_rdwr(UIO_READ, vp, buf, size, + off, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, + &resid, p)) != 0) { + DPRINTF(("Bad read error %d\n", error)); + return error; + } + /* + * See if we got all of it + */ + if (resid != 0) { + DPRINTF(("Incomplete read for header ask=%d, rem=%d\n", + size, resid)); + return error; + } + return 0; +} + + +/* + * linux_elf_load_file(): + * + * Load a file (interpreter/library) pointed to by path + * [stolen from coff_load_shlib()]. Made slightly more generic than + * the svr4 version, for possible later use in linux_uselib(). + */ +static int +linux_elf_load_file(p, path, vcset, entry, ap, last) + struct proc *p; + char *path; + struct exec_vmcmd_set *vcset; + u_long *entry; + struct elf_args *ap; + u_long *last; +{ + int error, i; + struct nameidata nd; + Elf32_Ehdr eh; + Elf32_Phdr *ph = NULL; + u_long phsize; + char *bp = NULL; + u_long addr = *last; + + DPRINTF(("Loading file %s @ %x\n", path, addr)); + + if ((error = linux_emul_find(p, NULL, linux_emul_path, path, &bp, 0)) != 0) + bp = NULL; + else + path = bp; + /* + * 1. open file + * 2. read filehdr + * 3. map text, data, and bss out of it using VM_* + */ + NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, p); + /* first get the vnode */ + if ((error = namei(&nd)) != 0) { + if (bp != NULL) + free((char *) bp, M_TEMP); + return error; + } + if ((error = linux_elf_read_from(nd.ni_vp, 0, p, (caddr_t) &eh, + sizeof(eh))) != 0) + goto bad; + +#ifdef DEBUG_EXEC_LINUX_ELF + print_Ehdr(&eh); +#endif + + if ((error = linux_elf_check_header(&eh, Elf32_et_dyn)) != 0) + goto bad; + + phsize = eh.e_phnum * sizeof(Elf32_Phdr); + ph = (Elf32_Phdr *) malloc(phsize, M_TEMP, M_WAITOK); + + if ((error = linux_elf_read_from(nd.ni_vp, eh.e_phoff, p, + (caddr_t) ph, phsize)) != 0) + goto bad; + + /* + * Load all the necessary sections + */ + for (i = 0; i < eh.e_phnum; i++) { + u_long size = 0; + int prot = 0; +#ifdef DEBUG_EXEC_LINUX_ELF + print_Phdr(&ph[i]); +#endif + + switch (ph[i].p_type) { + case Elf32_pt_load: + linux_elf_load_psection(vcset, nd.ni_vp, &ph[i], &addr, + &size, &prot); + /* Assume that the text segment is r-x only */ + if ((prot & PROT_WRITE) == 0) { + *entry = addr + eh.e_entry; + ap->arg_interp = addr; + DPRINTF(("Interpreter@ 0x%x\n", addr)); + } + addr += size; + break; + + case Elf32_pt_dynamic: + case Elf32_pt_phdr: + case Elf32_pt_note: + break; + + default: + DPRINTF(("interp: Unexpected program header type %d\n", + ph[i].p_type)); + break; + } + } + +bad: + if (ph != NULL) + free((char *) ph, M_TEMP); + if (bp != NULL) + free((char *) bp, M_TEMP); + + *last = addr; + vrele(nd.ni_vp); + return error; +} + + +/* + * exec_linux_elf_makecmds(): Prepare an Elf binary's exec package + * + * First, set of the various offsets/lengths in the exec package. + * + * Then, mark the text image busy (so it can be demand paged) or error + * out if this is not possible. Finally, set up vmcmds for the + * text, data, bss, and stack segments. + */ +int +exec_linux_elf_makecmds(p, epp) + struct proc *p; + struct exec_package *epp; +{ + Elf32_Ehdr *eh = epp->ep_hdr; + Elf32_Phdr *ph, *pp; + int error; + int i; + char interp[MAXPATHLEN]; + u_long pos = 0; + u_long phsize; + +#ifdef DEBUG_EXEC_LINUX_ELF + print_Ehdr(eh); +#endif + if (epp->ep_hdrvalid < sizeof(Elf32_Ehdr)) + return ENOEXEC; + + if (linux_elf_check_header(eh, Elf32_et_exec)) + return ENOEXEC; + + /* + * check if vnode is in open for writing, because we want to + * demand-page out of it. if it is, don't do it, for various + * reasons + */ + if (epp->ep_vp->v_writecount != 0) { +#ifdef DIAGNOSTIC + if (epp->ep_vp->v_flag & VTEXT) + panic("exec: a VTEXT vnode has writecount != 0\n"); +#endif + return ETXTBSY; + } + /* + * Allocate space to hold all the program headers, and read them + * from the file + */ + phsize = eh->e_phnum * sizeof(Elf32_Phdr); + ph = (Elf32_Phdr *) malloc(phsize, M_TEMP, M_WAITOK); + + if ((error = linux_elf_read_from(epp->ep_vp, eh->e_phoff, p, + (caddr_t) ph, phsize)) != 0) + goto bad; + + epp->ep_tsize = ~0; + epp->ep_dsize = ~0; + + interp[0] = '\0'; + + /* + * Load all the necessary sections + */ + for (i = 0; i < eh->e_phnum; i++) { + u_long addr = ~0, size = 0; + int prot = 0; + + pp = &ph[i]; +#ifdef DEBUG_EXEC_LINUX_ELF + print_Phdr(pp); +#endif + + switch (ph[i].p_type) { + case Elf32_pt_load: + linux_elf_load_psection(&epp->ep_vmcmds, epp->ep_vp, + &ph[i], &addr, &size, &prot); + if ((error = linux_elf_set_segment(epp, addr, size, + prot)) != 0) + goto bad; + break; + + case Elf32_pt_shlib: + DPRINTF(("No support for COFF libraries (yet)\n")); + error = ENOEXEC; + goto bad; + + case Elf32_pt_interp: + if (pp->p_filesz >= sizeof(interp)) { + DPRINTF(("Interpreter path too long %d\n", + pp->p_filesz)); + goto bad; + } + if ((error = linux_elf_read_from(epp->ep_vp, pp->p_offset, p, + (caddr_t) interp, pp->p_filesz)) != 0) + goto bad; + break; + + case Elf32_pt_dynamic: + case Elf32_pt_phdr: + case Elf32_pt_note: + break; + + default: + /* + * Not fatal, we don't need to understand everything + * :-) + */ + DPRINTF(("Unsupported program header type %d\n", + pp->p_type)); + break; + } + } + + /* + * Check if we found a dynamically linked binary and arrange to load + * it's interpreter + */ + if (interp[0]) { + struct elf_args *ap; + pos = ~0; + + ap = (struct elf_args *) malloc(sizeof(struct elf_args), + M_TEMP, M_WAITOK); + if ((error = linux_elf_load_file(p, interp, &epp->ep_vmcmds, + &epp->ep_entry, ap, &pos)) != 0) { + free((char *) ap, M_TEMP); + goto bad; + } + /* Arrange to load the program headers. */ + pos = LINUX_ELF_ALIGN(pos + NBPG, NBPG); + DPRINTF(("Program header @0x%x\n", pos)); + ap->arg_phaddr = pos; + NEW_VMCMD(&epp->ep_vmcmds, vmcmd_map_readvn, phsize, + pos, epp->ep_vp, eh->e_phoff, + VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE); + pos += phsize; + + ap->arg_phentsize = eh->e_phentsize; + ap->arg_phnum = eh->e_phnum; + ap->arg_entry = eh->e_entry; + + epp->ep_emul_arg = ap; + } else + epp->ep_entry = eh->e_entry; + + DPRINTF(("taddr 0x%x tsize 0x%x daddr 0x%x dsize 0x%x\n", + epp->ep_taddr, epp->ep_tsize, epp->ep_daddr, epp->ep_dsize)); + + free((char *) ph, M_TEMP); + + DPRINTF(("Elf entry@ 0x%x\n", epp->ep_entry)); + epp->ep_vp->v_flag |= VTEXT; + + epp->ep_emul = &emul_linux_elf; + + return exec_aout_setup_stack(p, epp); + +bad: + free((char *) ph, M_TEMP); + kill_vmcmds(&epp->ep_vmcmds); + return ENOEXEC; +} +/* + * The Linux system call to load shared libraries, a.out version. The + * a.out shared libs are just files that are mapped onto a fixed + * address in the process' address space. The address is given in * a_entry. Read in the header, set up some VM commands and run them. * * Yes, both text and data are mapped at once, so we're left with @@ -357,7 +995,7 @@ exec_linux_aout_prep_qmagic(p, epp) * Yuck. * * Because of the problem with ZMAGIC executables (text starts - * at 0x400 in the file, but needs t be mapped at 0), ZMAGIC + * at 0x400 in the file, but needs to be mapped at 0), ZMAGIC * shared libs are not handled very efficiently :-( */ @@ -399,6 +1037,9 @@ linux_uselib(p, uap, retval) return ENOEXEC; } + if (LINUX_N_MACHTYPE(&hdr) != LINUX_MID_MACHINE) + return ENOEXEC; + magic = LINUX_N_MAGIC(&hdr); taddr = hdr.a_entry & (~(NBPG - 1)); tsize = hdr.a_text; diff --git a/sys/compat/linux/common/linux_exec_elf32.c b/sys/compat/linux/common/linux_exec_elf32.c index cc6b07e40c0a..d707e86e6134 100644 --- a/sys/compat/linux/common/linux_exec_elf32.c +++ b/sys/compat/linux/common/linux_exec_elf32.c @@ -1,4 +1,4 @@ -/* $NetBSD: linux_exec_elf32.c,v 1.5 1995/05/16 14:19:07 mycroft Exp $ */ +/* $NetBSD: linux_exec_elf32.c,v 1.6 1995/06/11 14:56:47 fvdl Exp $ */ /* * Copyright (c) 1995 Frank van der Linden @@ -64,16 +64,43 @@ #include #include -static void *linux_copyargs __P((struct exec_package *, struct ps_strings *, - void *, void *)); +struct elf_args { + u_long arg_entry; /* progran entry point */ + u_long arg_interp; /* Interpreter load address */ + u_long arg_phaddr; /* program header address */ + u_long arg_phentsize; /* Size of program header */ + u_long arg_phnum; /* Number of program headers */ +}; -#define LINUX_AUX_ARGSIZ 2 +static void *linux_aout_copyargs __P((struct exec_package *, + struct ps_strings *, void *, void *)); +static void *linux_elf_copyargs __P((struct exec_package *, struct ps_strings *, + void *, void *)); +static int linux_elf_check_header __P((Elf32_Ehdr *, int)); +static void linux_elf_load_psection __P((struct exec_vmcmd_set *, + struct vnode *, Elf32_Phdr *, u_long *, u_long *, int *)); +static int linux_elf_set_segment __P((struct exec_package *, u_long, u_long, + int)); +static int linux_elf_read_from __P((struct vnode *, u_long, struct proc *, + caddr_t, int)); +static int linux_elf_load_file __P((struct proc *, char *, + struct exec_vmcmd_set *, u_long *, struct elf_args *, u_long *)); + +#ifdef DEBUG_EXEC_LINUX_ELF +#define DPRINTF(x) printf x +#else +#define DPRINTF(x) +#endif + +#define LINUX_ELF_ALIGN(a, b) ((a) & ~((b) - 1)) +#define LINUX_ELF_AUX_ARGSIZ (sizeof(AuxInfo) * 8 / sizeof(char *)) +#define LINUX_AOUT_AUX_ARGSIZ 2 extern int linux_error[]; extern struct sysent linux_sysent[]; extern char *linux_syscallnames[]; -struct emul emul_linux = { +struct emul emul_linux_aout = { "linux", linux_error, linux_sendsig, @@ -81,8 +108,23 @@ struct emul emul_linux = { LINUX_SYS_MAXSYSCALL, linux_sysent, linux_syscallnames, - LINUX_AUX_ARGSIZ, - linux_copyargs, + LINUX_AOUT_AUX_ARGSIZ, + linux_aout_copyargs, + setregs, + linux_sigcode, + linux_esigcode, +}; + +struct emul emul_linux_elf = { + "linux", + linux_error, + linux_sendsig, + LINUX_SYS_syscall, + LINUX_SYS_MAXSYSCALL, + linux_sysent, + linux_syscallnames, + LINUX_ELF_AUX_ARGSIZ, + linux_elf_copyargs, setregs, linux_sigcode, linux_esigcode, @@ -90,7 +132,7 @@ struct emul emul_linux = { static void * -linux_copyargs(pack, arginfo, stack, argp) +linux_aout_copyargs(pack, arginfo, stack, argp) struct exec_package *pack; struct ps_strings *arginfo; void *stack; @@ -142,6 +184,140 @@ linux_copyargs(pack, arginfo, stack, argp) return cpp; } +static void * +linux_elf_copyargs(pack, arginfo, stack, argp) + 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; + AuxInfo *a; + struct elf_args *ap; + + if (copyout(&argc, cpp++, sizeof(argc))) + return NULL; + + dp = (char *) (cpp + argc + envc + 2 + pack->ep_emul->e_arglen); + sp = argp; + + /* XXX don't copy them out, remap them! */ + arginfo->ps_argvstr = cpp; /* remember location of argv for later */ + + for (; --argc >= 0; sp += len, dp += len) + if (copyout(&dp, cpp++, sizeof(dp)) || + copyoutstr(sp, dp, ARG_MAX, &len)) + return NULL; + + if (copyout(&nullp, cpp++, sizeof(nullp))) + return NULL; + + arginfo->ps_envstr = cpp; /* remember location of envp for later */ + + for (; --envc >= 0; sp += len, dp += len) + if (copyout(&dp, cpp++, sizeof(dp)) || + copyoutstr(sp, dp, ARG_MAX, &len)) + return NULL; + + if (copyout(&nullp, cpp++, sizeof(nullp))) + return NULL; + + /* + * Push extra arguments on the stack needed by dynamically + * linked binaries + */ + a = (AuxInfo *) cpp; + if ((ap = (struct elf_args *) pack->ep_emul_arg)) { + + DPRINTF(("phaddr=0x%x, phsize=%d, phnum=%d, interp=0x%x, ", + ap->arg_phaddr, ap->arg_phentsize, ap->arg_phnum, + ap->arg_interp)); + DPRINTF((" entry=0x%x\n", ap->arg_entry)); + + a->au_id = AUX_phdr; + a->au_v = ap->arg_phaddr; + a++; + + a->au_id = AUX_phent; + a->au_v = ap->arg_phentsize; + a++; + + a->au_id = AUX_phnum; + a->au_v = ap->arg_phnum; + a++; + + a->au_id = AUX_pagesz; + a->au_v = NBPG; + a++; + + a->au_id = AUX_base; + a->au_v = ap->arg_interp; + a++; + + a->au_id = AUX_flags; + a->au_v = 0; + a++; + + a->au_id = AUX_entry; + a->au_v = ap->arg_entry; + a++; + + a->au_id = AUX_null; + a->au_v = 0; + a++; + + free((char *) ap, M_TEMP); + } + return a; +} + +#ifdef DEBUG_EXEC_LINUX_ELF +static void +print_Ehdr(e) + Elf32_Ehdr *e; +{ + printf("e_ident %s, ", e->e_ident); + printf("e_type %d, ", e->e_type); + printf("e_machine %d, ", e->e_machine); + printf("e_version %ld, ", e->e_version); + printf("e_entry %lx, ", e->e_entry); + printf("e_phoff %lx, ", e->e_phoff); + printf("e_shoff %lx, ", e->e_shoff); + printf("e_flags %lx, ", e->e_flags); + printf("e_ehsize %d, ", e->e_ehsize); + printf("e_phentsize %d, ", e->e_phentsize); + printf("e_phnum %d, ", e->e_phnum); + printf("e_shentsize %d, ", e->e_shentsize); + printf("e_shnum %d, ", e->e_shnum); + printf("e_shstrndx %d\n", e->e_shstrndx); +} + + +static void +print_Phdr(p) + Elf32_Phdr *p; +{ + static char *types[] = + { + "null", "load", "dynamic", "interp", + "note", "shlib", "phdr", "entry7" + }; + + printf("p_type %ld [%s], ", p->p_type, types[p->p_type & 7]); + printf("p_offset %lx, ", p->p_offset); + printf("p_vaddr %lx, ", p->p_vaddr); + printf("p_paddr %lx, ", p->p_paddr); + printf("p_filesz %ld, ", p->p_filesz); + printf("p_memsz %ld, ", p->p_memsz); + printf("p_flags %lx, ", p->p_flags); + printf("p_align %ld\n", p->p_align); +} +#endif int exec_linux_aout_makecmds(p, epp) @@ -174,7 +350,7 @@ exec_linux_aout_makecmds(p, epp) break; } if (error == 0) - epp->ep_emul = &emul_linux; + epp->ep_emul = &emul_linux_aout; return error; } @@ -345,9 +521,471 @@ exec_linux_aout_prep_qmagic(p, epp) } /* - * The Linux system call to load shared libraries. The current shared - * libraries are just (QMAGIC) a.out files that are mapped onto a fixed - * address * in the process' address space. The address is given in + * linux_elf_check_header(): + * + * Check header for validity; return 0 of ok ENOEXEC if error + */ +static int +linux_elf_check_header(eh, type) + Elf32_Ehdr *eh; + int type; +{ +#ifdef sparc + /* #$%@#$%@#$%! */ +# define memcmp bcmp +#endif + if (memcmp(eh->e_ident, Elf32_e_ident, Elf32_e_siz) != 0) { + DPRINTF(("Not an elf file\n")); + return ENOEXEC; + } + + switch (eh->e_machine) { +#ifdef i386 + case Elf32_em_386: + case Elf32_em_486: +#endif +#ifdef sparc + case Elf32_em_sparc: +#endif + break; + + default: + DPRINTF(("Unsupported elf machine type %d\n", eh->e_machine)); + return ENOEXEC; + } + + if (eh->e_type != type) { + DPRINTF(("Not an elf executable\n")); + return ENOEXEC; + } + + return 0; +} + + +/* + * linux_elf_load_psection(): + * + * Load a psection at the appropriate address + */ +static void +linux_elf_load_psection(vcset, vp, ph, addr, size, prot) + struct exec_vmcmd_set *vcset; + struct vnode *vp; + Elf32_Phdr *ph; + u_long *addr; + u_long *size; + int *prot; +{ + u_long uaddr; + long diff; + long offset; + u_long msize; + + /* + * If the user specified an address, then we load there. + */ + if (*addr != ~0) { + uaddr = *addr + ph->p_align; + *addr = LINUX_ELF_ALIGN(uaddr, ph->p_align); + uaddr = LINUX_ELF_ALIGN(ph->p_vaddr, ph->p_align); + diff = ph->p_vaddr - uaddr; + } else { + uaddr = ph->p_vaddr; + *addr = LINUX_ELF_ALIGN(uaddr, ph->p_align); + diff = uaddr - *addr; + } + + *prot |= (ph->p_flags & Elf32_pf_r) ? VM_PROT_READ : 0; + *prot |= (ph->p_flags & Elf32_pf_w) ? VM_PROT_WRITE : 0; + *prot |= (ph->p_flags & Elf32_pf_x) ? VM_PROT_EXECUTE : 0; + + offset = ph->p_offset - diff; + *size = ph->p_filesz + diff; + msize = ph->p_memsz + diff; + + DPRINTF(("Elf Seg@ 0x%x/0x%x sz %d/%d off 0x%x/0x%x[%d] algn 0x%x\n", + ph->p_vaddr, *addr, *size, msize, ph->p_offset, offset, + diff, ph->p_align)); + + NEW_VMCMD(vcset, vmcmd_map_readvn, *size, + *addr, vp, offset, *prot); + + /* + * Check if we need to extend the size of the segment + */ + { + u_long rm = round_page(*addr + msize); + u_long rf = round_page(*addr + *size); + if (rm != rf) { + DPRINTF(("zeropad 0x%x-0x%x\n", rf, rm)); + NEW_VMCMD(vcset, vmcmd_map_zero, rm - rf, + rf, NULLVP, 0, *prot); + *size = msize; + } + } +} + + +/* + * linux_elf_set_segment(): + * + * Decide if the segment is text or data, depending on the protection + * and set it appropriately + */ +static int +linux_elf_set_segment(epp, vaddr, size, prot) + struct exec_package *epp; + u_long vaddr; + u_long size; + int prot; +{ + /* + * Kludge: Unfortunately the current implementation of + * exec package assumes a single text and data segment. + * In Elf we can have more, but here we limit ourselves + * to two and hope :-( + * We also assume that the text is r-x, and data is rwx. + */ + switch (prot) { + case (VM_PROT_READ | VM_PROT_EXECUTE): + if (epp->ep_tsize != ~0) { + DPRINTF(("More than one text segment\n")); + return ENOEXEC; + } + epp->ep_taddr = vaddr; + epp->ep_tsize = size; + DPRINTF(("Elf Text@ 0x%x, size %d\n", vaddr, size)); + break; + + case (VM_PROT_READ | VM_PROT_WRITE): + case (VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE): + if (epp->ep_dsize != ~0) { + DPRINTF(("More than one data segment\n")); + return ENOEXEC; + } + epp->ep_daddr = vaddr; + epp->ep_dsize = size; + + DPRINTF(("Elf Data@ 0x%x, size %d\n", vaddr, size)); + break; + + default: + DPRINTF(("Bad protection 0%o\n", prot)); + return ENOEXEC; + } + return 0; +} + + +/* + * linux_elf_read_from(): + * + * Read from vnode into buffer at offset. + */ +static int +linux_elf_read_from(vp, off, p, buf, size) + struct vnode *vp; + u_long off; + struct proc *p; + caddr_t buf; + int size; +{ + int error; + int resid; + + DPRINTF(("read from 0x%x to 0x%x size %d\n", + off, buf, size)); + if ((error = vn_rdwr(UIO_READ, vp, buf, size, + off, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, + &resid, p)) != 0) { + DPRINTF(("Bad read error %d\n", error)); + return error; + } + /* + * See if we got all of it + */ + if (resid != 0) { + DPRINTF(("Incomplete read for header ask=%d, rem=%d\n", + size, resid)); + return error; + } + return 0; +} + + +/* + * linux_elf_load_file(): + * + * Load a file (interpreter/library) pointed to by path + * [stolen from coff_load_shlib()]. Made slightly more generic than + * the svr4 version, for possible later use in linux_uselib(). + */ +static int +linux_elf_load_file(p, path, vcset, entry, ap, last) + struct proc *p; + char *path; + struct exec_vmcmd_set *vcset; + u_long *entry; + struct elf_args *ap; + u_long *last; +{ + int error, i; + struct nameidata nd; + Elf32_Ehdr eh; + Elf32_Phdr *ph = NULL; + u_long phsize; + char *bp = NULL; + u_long addr = *last; + + DPRINTF(("Loading file %s @ %x\n", path, addr)); + + if ((error = linux_emul_find(p, NULL, linux_emul_path, path, &bp, 0)) != 0) + bp = NULL; + else + path = bp; + /* + * 1. open file + * 2. read filehdr + * 3. map text, data, and bss out of it using VM_* + */ + NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, p); + /* first get the vnode */ + if ((error = namei(&nd)) != 0) { + if (bp != NULL) + free((char *) bp, M_TEMP); + return error; + } + if ((error = linux_elf_read_from(nd.ni_vp, 0, p, (caddr_t) &eh, + sizeof(eh))) != 0) + goto bad; + +#ifdef DEBUG_EXEC_LINUX_ELF + print_Ehdr(&eh); +#endif + + if ((error = linux_elf_check_header(&eh, Elf32_et_dyn)) != 0) + goto bad; + + phsize = eh.e_phnum * sizeof(Elf32_Phdr); + ph = (Elf32_Phdr *) malloc(phsize, M_TEMP, M_WAITOK); + + if ((error = linux_elf_read_from(nd.ni_vp, eh.e_phoff, p, + (caddr_t) ph, phsize)) != 0) + goto bad; + + /* + * Load all the necessary sections + */ + for (i = 0; i < eh.e_phnum; i++) { + u_long size = 0; + int prot = 0; +#ifdef DEBUG_EXEC_LINUX_ELF + print_Phdr(&ph[i]); +#endif + + switch (ph[i].p_type) { + case Elf32_pt_load: + linux_elf_load_psection(vcset, nd.ni_vp, &ph[i], &addr, + &size, &prot); + /* Assume that the text segment is r-x only */ + if ((prot & PROT_WRITE) == 0) { + *entry = addr + eh.e_entry; + ap->arg_interp = addr; + DPRINTF(("Interpreter@ 0x%x\n", addr)); + } + addr += size; + break; + + case Elf32_pt_dynamic: + case Elf32_pt_phdr: + case Elf32_pt_note: + break; + + default: + DPRINTF(("interp: Unexpected program header type %d\n", + ph[i].p_type)); + break; + } + } + +bad: + if (ph != NULL) + free((char *) ph, M_TEMP); + if (bp != NULL) + free((char *) bp, M_TEMP); + + *last = addr; + vrele(nd.ni_vp); + return error; +} + + +/* + * exec_linux_elf_makecmds(): Prepare an Elf binary's exec package + * + * First, set of the various offsets/lengths in the exec package. + * + * Then, mark the text image busy (so it can be demand paged) or error + * out if this is not possible. Finally, set up vmcmds for the + * text, data, bss, and stack segments. + */ +int +exec_linux_elf_makecmds(p, epp) + struct proc *p; + struct exec_package *epp; +{ + Elf32_Ehdr *eh = epp->ep_hdr; + Elf32_Phdr *ph, *pp; + int error; + int i; + char interp[MAXPATHLEN]; + u_long pos = 0; + u_long phsize; + +#ifdef DEBUG_EXEC_LINUX_ELF + print_Ehdr(eh); +#endif + if (epp->ep_hdrvalid < sizeof(Elf32_Ehdr)) + return ENOEXEC; + + if (linux_elf_check_header(eh, Elf32_et_exec)) + return ENOEXEC; + + /* + * check if vnode is in open for writing, because we want to + * demand-page out of it. if it is, don't do it, for various + * reasons + */ + if (epp->ep_vp->v_writecount != 0) { +#ifdef DIAGNOSTIC + if (epp->ep_vp->v_flag & VTEXT) + panic("exec: a VTEXT vnode has writecount != 0\n"); +#endif + return ETXTBSY; + } + /* + * Allocate space to hold all the program headers, and read them + * from the file + */ + phsize = eh->e_phnum * sizeof(Elf32_Phdr); + ph = (Elf32_Phdr *) malloc(phsize, M_TEMP, M_WAITOK); + + if ((error = linux_elf_read_from(epp->ep_vp, eh->e_phoff, p, + (caddr_t) ph, phsize)) != 0) + goto bad; + + epp->ep_tsize = ~0; + epp->ep_dsize = ~0; + + interp[0] = '\0'; + + /* + * Load all the necessary sections + */ + for (i = 0; i < eh->e_phnum; i++) { + u_long addr = ~0, size = 0; + int prot = 0; + + pp = &ph[i]; +#ifdef DEBUG_EXEC_LINUX_ELF + print_Phdr(pp); +#endif + + switch (ph[i].p_type) { + case Elf32_pt_load: + linux_elf_load_psection(&epp->ep_vmcmds, epp->ep_vp, + &ph[i], &addr, &size, &prot); + if ((error = linux_elf_set_segment(epp, addr, size, + prot)) != 0) + goto bad; + break; + + case Elf32_pt_shlib: + DPRINTF(("No support for COFF libraries (yet)\n")); + error = ENOEXEC; + goto bad; + + case Elf32_pt_interp: + if (pp->p_filesz >= sizeof(interp)) { + DPRINTF(("Interpreter path too long %d\n", + pp->p_filesz)); + goto bad; + } + if ((error = linux_elf_read_from(epp->ep_vp, pp->p_offset, p, + (caddr_t) interp, pp->p_filesz)) != 0) + goto bad; + break; + + case Elf32_pt_dynamic: + case Elf32_pt_phdr: + case Elf32_pt_note: + break; + + default: + /* + * Not fatal, we don't need to understand everything + * :-) + */ + DPRINTF(("Unsupported program header type %d\n", + pp->p_type)); + break; + } + } + + /* + * Check if we found a dynamically linked binary and arrange to load + * it's interpreter + */ + if (interp[0]) { + struct elf_args *ap; + pos = ~0; + + ap = (struct elf_args *) malloc(sizeof(struct elf_args), + M_TEMP, M_WAITOK); + if ((error = linux_elf_load_file(p, interp, &epp->ep_vmcmds, + &epp->ep_entry, ap, &pos)) != 0) { + free((char *) ap, M_TEMP); + goto bad; + } + /* Arrange to load the program headers. */ + pos = LINUX_ELF_ALIGN(pos + NBPG, NBPG); + DPRINTF(("Program header @0x%x\n", pos)); + ap->arg_phaddr = pos; + NEW_VMCMD(&epp->ep_vmcmds, vmcmd_map_readvn, phsize, + pos, epp->ep_vp, eh->e_phoff, + VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE); + pos += phsize; + + ap->arg_phentsize = eh->e_phentsize; + ap->arg_phnum = eh->e_phnum; + ap->arg_entry = eh->e_entry; + + epp->ep_emul_arg = ap; + } else + epp->ep_entry = eh->e_entry; + + DPRINTF(("taddr 0x%x tsize 0x%x daddr 0x%x dsize 0x%x\n", + epp->ep_taddr, epp->ep_tsize, epp->ep_daddr, epp->ep_dsize)); + + free((char *) ph, M_TEMP); + + DPRINTF(("Elf entry@ 0x%x\n", epp->ep_entry)); + epp->ep_vp->v_flag |= VTEXT; + + epp->ep_emul = &emul_linux_elf; + + return exec_aout_setup_stack(p, epp); + +bad: + free((char *) ph, M_TEMP); + kill_vmcmds(&epp->ep_vmcmds); + return ENOEXEC; +} +/* + * The Linux system call to load shared libraries, a.out version. The + * a.out shared libs are just files that are mapped onto a fixed + * address in the process' address space. The address is given in * a_entry. Read in the header, set up some VM commands and run them. * * Yes, both text and data are mapped at once, so we're left with @@ -357,7 +995,7 @@ exec_linux_aout_prep_qmagic(p, epp) * Yuck. * * Because of the problem with ZMAGIC executables (text starts - * at 0x400 in the file, but needs t be mapped at 0), ZMAGIC + * at 0x400 in the file, but needs to be mapped at 0), ZMAGIC * shared libs are not handled very efficiently :-( */ @@ -399,6 +1037,9 @@ linux_uselib(p, uap, retval) return ENOEXEC; } + if (LINUX_N_MACHTYPE(&hdr) != LINUX_MID_MACHINE) + return ENOEXEC; + magic = LINUX_N_MAGIC(&hdr); taddr = hdr.a_entry & (~(NBPG - 1)); tsize = hdr.a_text; diff --git a/sys/compat/linux/common/linux_misc.c b/sys/compat/linux/common/linux_misc.c index 9109c6496cad..dceb119675de 100644 --- a/sys/compat/linux/common/linux_misc.c +++ b/sys/compat/linux/common/linux_misc.c @@ -1,4 +1,4 @@ -/* $NetBSD: linux_misc.c,v 1.5 1995/06/10 22:19:14 mycroft Exp $ */ +/* $NetBSD: linux_misc.c,v 1.6 1995/06/11 14:56:59 fvdl Exp $ */ /* * Copyright (c) 1995 Frank van der Linden @@ -789,3 +789,23 @@ linux_getpgid(p, uap, retval) retval[0] = targp->p_pgid; return 0; } + +/* + * Set the 'personality' (emulation mode) for the current process. Only + * accept the Linux personality here (0). This call is needed because + * the Linux ELF crt0 issues it in an ugly kludge to make sure that + * ELF binaries run in Linux mode, not SVR4 mode. + */ +int +linux_personality(p, uap, retval) + struct proc *p; + struct linux_personality_args /* P + syscallarg(int) per; + } */ *uap; + register_t *retval; +{ + if (SCARG(uap, per) != 0) + return EINVAL; + retval[0] = 0; + return 0; +} diff --git a/sys/compat/linux/common/linux_misc_notalpha.c b/sys/compat/linux/common/linux_misc_notalpha.c index 11710e762f53..386297902488 100644 --- a/sys/compat/linux/common/linux_misc_notalpha.c +++ b/sys/compat/linux/common/linux_misc_notalpha.c @@ -1,4 +1,4 @@ -/* $NetBSD: linux_misc_notalpha.c,v 1.5 1995/06/10 22:19:14 mycroft Exp $ */ +/* $NetBSD: linux_misc_notalpha.c,v 1.6 1995/06/11 14:56:59 fvdl Exp $ */ /* * Copyright (c) 1995 Frank van der Linden @@ -789,3 +789,23 @@ linux_getpgid(p, uap, retval) retval[0] = targp->p_pgid; return 0; } + +/* + * Set the 'personality' (emulation mode) for the current process. Only + * accept the Linux personality here (0). This call is needed because + * the Linux ELF crt0 issues it in an ugly kludge to make sure that + * ELF binaries run in Linux mode, not SVR4 mode. + */ +int +linux_personality(p, uap, retval) + struct proc *p; + struct linux_personality_args /* P + syscallarg(int) per; + } */ *uap; + register_t *retval; +{ + if (SCARG(uap, per) != 0) + return EINVAL; + retval[0] = 0; + return 0; +} diff --git a/sys/compat/linux/common/linux_oldmmap.c b/sys/compat/linux/common/linux_oldmmap.c index ee5f4fb0c6f8..de564fc3732d 100644 --- a/sys/compat/linux/common/linux_oldmmap.c +++ b/sys/compat/linux/common/linux_oldmmap.c @@ -1,4 +1,4 @@ -/* $NetBSD: linux_oldmmap.c,v 1.5 1995/06/10 22:19:14 mycroft Exp $ */ +/* $NetBSD: linux_oldmmap.c,v 1.6 1995/06/11 14:56:59 fvdl Exp $ */ /* * Copyright (c) 1995 Frank van der Linden @@ -789,3 +789,23 @@ linux_getpgid(p, uap, retval) retval[0] = targp->p_pgid; return 0; } + +/* + * Set the 'personality' (emulation mode) for the current process. Only + * accept the Linux personality here (0). This call is needed because + * the Linux ELF crt0 issues it in an ugly kludge to make sure that + * ELF binaries run in Linux mode, not SVR4 mode. + */ +int +linux_personality(p, uap, retval) + struct proc *p; + struct linux_personality_args /* P + syscallarg(int) per; + } */ *uap; + register_t *retval; +{ + if (SCARG(uap, per) != 0) + return EINVAL; + retval[0] = 0; + return 0; +} diff --git a/sys/compat/linux/common/linux_oldolduname.c b/sys/compat/linux/common/linux_oldolduname.c index 74e2c43b5bbd..c86a02fb30ba 100644 --- a/sys/compat/linux/common/linux_oldolduname.c +++ b/sys/compat/linux/common/linux_oldolduname.c @@ -1,4 +1,4 @@ -/* $NetBSD: linux_oldolduname.c,v 1.5 1995/06/10 22:19:14 mycroft Exp $ */ +/* $NetBSD: linux_oldolduname.c,v 1.6 1995/06/11 14:56:59 fvdl Exp $ */ /* * Copyright (c) 1995 Frank van der Linden @@ -789,3 +789,23 @@ linux_getpgid(p, uap, retval) retval[0] = targp->p_pgid; return 0; } + +/* + * Set the 'personality' (emulation mode) for the current process. Only + * accept the Linux personality here (0). This call is needed because + * the Linux ELF crt0 issues it in an ugly kludge to make sure that + * ELF binaries run in Linux mode, not SVR4 mode. + */ +int +linux_personality(p, uap, retval) + struct proc *p; + struct linux_personality_args /* P + syscallarg(int) per; + } */ *uap; + register_t *retval; +{ + if (SCARG(uap, per) != 0) + return EINVAL; + retval[0] = 0; + return 0; +} diff --git a/sys/compat/linux/common/linux_oldselect.c b/sys/compat/linux/common/linux_oldselect.c index 0850965ed315..29539cd62841 100644 --- a/sys/compat/linux/common/linux_oldselect.c +++ b/sys/compat/linux/common/linux_oldselect.c @@ -1,4 +1,4 @@ -/* $NetBSD: linux_oldselect.c,v 1.5 1995/06/10 22:19:14 mycroft Exp $ */ +/* $NetBSD: linux_oldselect.c,v 1.6 1995/06/11 14:56:59 fvdl Exp $ */ /* * Copyright (c) 1995 Frank van der Linden @@ -789,3 +789,23 @@ linux_getpgid(p, uap, retval) retval[0] = targp->p_pgid; return 0; } + +/* + * Set the 'personality' (emulation mode) for the current process. Only + * accept the Linux personality here (0). This call is needed because + * the Linux ELF crt0 issues it in an ugly kludge to make sure that + * ELF binaries run in Linux mode, not SVR4 mode. + */ +int +linux_personality(p, uap, retval) + struct proc *p; + struct linux_personality_args /* P + syscallarg(int) per; + } */ *uap; + register_t *retval; +{ + if (SCARG(uap, per) != 0) + return EINVAL; + retval[0] = 0; + return 0; +} diff --git a/sys/compat/linux/common/linux_olduname.c b/sys/compat/linux/common/linux_olduname.c index 20b3e371f766..2ab31c99c303 100644 --- a/sys/compat/linux/common/linux_olduname.c +++ b/sys/compat/linux/common/linux_olduname.c @@ -1,4 +1,4 @@ -/* $NetBSD: linux_olduname.c,v 1.5 1995/06/10 22:19:14 mycroft Exp $ */ +/* $NetBSD: linux_olduname.c,v 1.6 1995/06/11 14:56:59 fvdl Exp $ */ /* * Copyright (c) 1995 Frank van der Linden @@ -789,3 +789,23 @@ linux_getpgid(p, uap, retval) retval[0] = targp->p_pgid; return 0; } + +/* + * Set the 'personality' (emulation mode) for the current process. Only + * accept the Linux personality here (0). This call is needed because + * the Linux ELF crt0 issues it in an ugly kludge to make sure that + * ELF binaries run in Linux mode, not SVR4 mode. + */ +int +linux_personality(p, uap, retval) + struct proc *p; + struct linux_personality_args /* P + syscallarg(int) per; + } */ *uap; + register_t *retval; +{ + if (SCARG(uap, per) != 0) + return EINVAL; + retval[0] = 0; + return 0; +} diff --git a/sys/compat/linux/common/linux_pipe.c b/sys/compat/linux/common/linux_pipe.c index 8a3bdd880155..978f63edb30b 100644 --- a/sys/compat/linux/common/linux_pipe.c +++ b/sys/compat/linux/common/linux_pipe.c @@ -1,4 +1,4 @@ -/* $NetBSD: linux_pipe.c,v 1.5 1995/06/10 22:19:14 mycroft Exp $ */ +/* $NetBSD: linux_pipe.c,v 1.6 1995/06/11 14:56:59 fvdl Exp $ */ /* * Copyright (c) 1995 Frank van der Linden @@ -789,3 +789,23 @@ linux_getpgid(p, uap, retval) retval[0] = targp->p_pgid; return 0; } + +/* + * Set the 'personality' (emulation mode) for the current process. Only + * accept the Linux personality here (0). This call is needed because + * the Linux ELF crt0 issues it in an ugly kludge to make sure that + * ELF binaries run in Linux mode, not SVR4 mode. + */ +int +linux_personality(p, uap, retval) + struct proc *p; + struct linux_personality_args /* P + syscallarg(int) per; + } */ *uap; + register_t *retval; +{ + if (SCARG(uap, per) != 0) + return EINVAL; + retval[0] = 0; + return 0; +} diff --git a/sys/compat/linux/i386/syscalls.master b/sys/compat/linux/i386/syscalls.master index 609123ba74bf..cd4bca11b659 100644 --- a/sys/compat/linux/i386/syscalls.master +++ b/sys/compat/linux/i386/syscalls.master @@ -1,4 +1,4 @@ - $NetBSD: syscalls.master,v 1.5 1995/05/06 18:16:40 mycroft Exp $ + $NetBSD: syscalls.master,v 1.6 1995/06/11 14:57:01 fvdl Exp $ ; @(#)syscalls.master 8.1 (Berkeley) 7/19/93 @@ -194,7 +194,7 @@ 133 NOARGS { int fchdir(int fd); } 134 UNIMPL linux_bdflush 135 UNIMPL linux_sysfs -136 UNIMPL linux_personality +136 STD { int linux_personality(int per); } 137 UNIMPL linux_afs_syscall 138 UNIMPL linux_setfsuid 139 UNIMPL linux_getfsuid diff --git a/sys/compat/linux/include/linux_exec.h b/sys/compat/linux/include/linux_exec.h index 4bd97aeb20cd..4645b901a74c 100644 --- a/sys/compat/linux/include/linux_exec.h +++ b/sys/compat/linux/include/linux_exec.h @@ -1,4 +1,4 @@ -/* $NetBSD: linux_exec.h,v 1.2 1995/04/07 22:23:26 fvdl Exp $ */ +/* $NetBSD: linux_exec.h,v 1.3 1995/06/11 14:56:56 fvdl Exp $ */ /* * Copyright (c) 1995 Frank van der Linden @@ -35,6 +35,12 @@ #ifndef _LINUX_EXEC_H #define _LINUX_EXEC_H +/* + * Include this for ELF definitions. Should be an emul-independent file + * someday. + */ +#include + #define LINUX_M_I386 100 /* Sparc? Alpha? */ @@ -66,6 +72,7 @@ #define LINUX_N_BSSADDR(x,m) (LINUX_N_DATADDR(x,m) + (x).a_data) int exec_linux_aout_makecmds __P((struct proc *, struct exec_package *)); +int exec_linux_elf_makecmds __P((struct proc *, struct exec_package *)); extern char linux_sigcode[], linux_esigcode[]; diff --git a/sys/compat/linux/linux_exec.c b/sys/compat/linux/linux_exec.c index 99792655576b..64c6b6963a90 100644 --- a/sys/compat/linux/linux_exec.c +++ b/sys/compat/linux/linux_exec.c @@ -1,4 +1,4 @@ -/* $NetBSD: linux_exec.c,v 1.5 1995/05/16 14:19:07 mycroft Exp $ */ +/* $NetBSD: linux_exec.c,v 1.6 1995/06/11 14:56:47 fvdl Exp $ */ /* * Copyright (c) 1995 Frank van der Linden @@ -64,16 +64,43 @@ #include #include -static void *linux_copyargs __P((struct exec_package *, struct ps_strings *, - void *, void *)); +struct elf_args { + u_long arg_entry; /* progran entry point */ + u_long arg_interp; /* Interpreter load address */ + u_long arg_phaddr; /* program header address */ + u_long arg_phentsize; /* Size of program header */ + u_long arg_phnum; /* Number of program headers */ +}; -#define LINUX_AUX_ARGSIZ 2 +static void *linux_aout_copyargs __P((struct exec_package *, + struct ps_strings *, void *, void *)); +static void *linux_elf_copyargs __P((struct exec_package *, struct ps_strings *, + void *, void *)); +static int linux_elf_check_header __P((Elf32_Ehdr *, int)); +static void linux_elf_load_psection __P((struct exec_vmcmd_set *, + struct vnode *, Elf32_Phdr *, u_long *, u_long *, int *)); +static int linux_elf_set_segment __P((struct exec_package *, u_long, u_long, + int)); +static int linux_elf_read_from __P((struct vnode *, u_long, struct proc *, + caddr_t, int)); +static int linux_elf_load_file __P((struct proc *, char *, + struct exec_vmcmd_set *, u_long *, struct elf_args *, u_long *)); + +#ifdef DEBUG_EXEC_LINUX_ELF +#define DPRINTF(x) printf x +#else +#define DPRINTF(x) +#endif + +#define LINUX_ELF_ALIGN(a, b) ((a) & ~((b) - 1)) +#define LINUX_ELF_AUX_ARGSIZ (sizeof(AuxInfo) * 8 / sizeof(char *)) +#define LINUX_AOUT_AUX_ARGSIZ 2 extern int linux_error[]; extern struct sysent linux_sysent[]; extern char *linux_syscallnames[]; -struct emul emul_linux = { +struct emul emul_linux_aout = { "linux", linux_error, linux_sendsig, @@ -81,8 +108,23 @@ struct emul emul_linux = { LINUX_SYS_MAXSYSCALL, linux_sysent, linux_syscallnames, - LINUX_AUX_ARGSIZ, - linux_copyargs, + LINUX_AOUT_AUX_ARGSIZ, + linux_aout_copyargs, + setregs, + linux_sigcode, + linux_esigcode, +}; + +struct emul emul_linux_elf = { + "linux", + linux_error, + linux_sendsig, + LINUX_SYS_syscall, + LINUX_SYS_MAXSYSCALL, + linux_sysent, + linux_syscallnames, + LINUX_ELF_AUX_ARGSIZ, + linux_elf_copyargs, setregs, linux_sigcode, linux_esigcode, @@ -90,7 +132,7 @@ struct emul emul_linux = { static void * -linux_copyargs(pack, arginfo, stack, argp) +linux_aout_copyargs(pack, arginfo, stack, argp) struct exec_package *pack; struct ps_strings *arginfo; void *stack; @@ -142,6 +184,140 @@ linux_copyargs(pack, arginfo, stack, argp) return cpp; } +static void * +linux_elf_copyargs(pack, arginfo, stack, argp) + 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; + AuxInfo *a; + struct elf_args *ap; + + if (copyout(&argc, cpp++, sizeof(argc))) + return NULL; + + dp = (char *) (cpp + argc + envc + 2 + pack->ep_emul->e_arglen); + sp = argp; + + /* XXX don't copy them out, remap them! */ + arginfo->ps_argvstr = cpp; /* remember location of argv for later */ + + for (; --argc >= 0; sp += len, dp += len) + if (copyout(&dp, cpp++, sizeof(dp)) || + copyoutstr(sp, dp, ARG_MAX, &len)) + return NULL; + + if (copyout(&nullp, cpp++, sizeof(nullp))) + return NULL; + + arginfo->ps_envstr = cpp; /* remember location of envp for later */ + + for (; --envc >= 0; sp += len, dp += len) + if (copyout(&dp, cpp++, sizeof(dp)) || + copyoutstr(sp, dp, ARG_MAX, &len)) + return NULL; + + if (copyout(&nullp, cpp++, sizeof(nullp))) + return NULL; + + /* + * Push extra arguments on the stack needed by dynamically + * linked binaries + */ + a = (AuxInfo *) cpp; + if ((ap = (struct elf_args *) pack->ep_emul_arg)) { + + DPRINTF(("phaddr=0x%x, phsize=%d, phnum=%d, interp=0x%x, ", + ap->arg_phaddr, ap->arg_phentsize, ap->arg_phnum, + ap->arg_interp)); + DPRINTF((" entry=0x%x\n", ap->arg_entry)); + + a->au_id = AUX_phdr; + a->au_v = ap->arg_phaddr; + a++; + + a->au_id = AUX_phent; + a->au_v = ap->arg_phentsize; + a++; + + a->au_id = AUX_phnum; + a->au_v = ap->arg_phnum; + a++; + + a->au_id = AUX_pagesz; + a->au_v = NBPG; + a++; + + a->au_id = AUX_base; + a->au_v = ap->arg_interp; + a++; + + a->au_id = AUX_flags; + a->au_v = 0; + a++; + + a->au_id = AUX_entry; + a->au_v = ap->arg_entry; + a++; + + a->au_id = AUX_null; + a->au_v = 0; + a++; + + free((char *) ap, M_TEMP); + } + return a; +} + +#ifdef DEBUG_EXEC_LINUX_ELF +static void +print_Ehdr(e) + Elf32_Ehdr *e; +{ + printf("e_ident %s, ", e->e_ident); + printf("e_type %d, ", e->e_type); + printf("e_machine %d, ", e->e_machine); + printf("e_version %ld, ", e->e_version); + printf("e_entry %lx, ", e->e_entry); + printf("e_phoff %lx, ", e->e_phoff); + printf("e_shoff %lx, ", e->e_shoff); + printf("e_flags %lx, ", e->e_flags); + printf("e_ehsize %d, ", e->e_ehsize); + printf("e_phentsize %d, ", e->e_phentsize); + printf("e_phnum %d, ", e->e_phnum); + printf("e_shentsize %d, ", e->e_shentsize); + printf("e_shnum %d, ", e->e_shnum); + printf("e_shstrndx %d\n", e->e_shstrndx); +} + + +static void +print_Phdr(p) + Elf32_Phdr *p; +{ + static char *types[] = + { + "null", "load", "dynamic", "interp", + "note", "shlib", "phdr", "entry7" + }; + + printf("p_type %ld [%s], ", p->p_type, types[p->p_type & 7]); + printf("p_offset %lx, ", p->p_offset); + printf("p_vaddr %lx, ", p->p_vaddr); + printf("p_paddr %lx, ", p->p_paddr); + printf("p_filesz %ld, ", p->p_filesz); + printf("p_memsz %ld, ", p->p_memsz); + printf("p_flags %lx, ", p->p_flags); + printf("p_align %ld\n", p->p_align); +} +#endif int exec_linux_aout_makecmds(p, epp) @@ -174,7 +350,7 @@ exec_linux_aout_makecmds(p, epp) break; } if (error == 0) - epp->ep_emul = &emul_linux; + epp->ep_emul = &emul_linux_aout; return error; } @@ -345,9 +521,471 @@ exec_linux_aout_prep_qmagic(p, epp) } /* - * The Linux system call to load shared libraries. The current shared - * libraries are just (QMAGIC) a.out files that are mapped onto a fixed - * address * in the process' address space. The address is given in + * linux_elf_check_header(): + * + * Check header for validity; return 0 of ok ENOEXEC if error + */ +static int +linux_elf_check_header(eh, type) + Elf32_Ehdr *eh; + int type; +{ +#ifdef sparc + /* #$%@#$%@#$%! */ +# define memcmp bcmp +#endif + if (memcmp(eh->e_ident, Elf32_e_ident, Elf32_e_siz) != 0) { + DPRINTF(("Not an elf file\n")); + return ENOEXEC; + } + + switch (eh->e_machine) { +#ifdef i386 + case Elf32_em_386: + case Elf32_em_486: +#endif +#ifdef sparc + case Elf32_em_sparc: +#endif + break; + + default: + DPRINTF(("Unsupported elf machine type %d\n", eh->e_machine)); + return ENOEXEC; + } + + if (eh->e_type != type) { + DPRINTF(("Not an elf executable\n")); + return ENOEXEC; + } + + return 0; +} + + +/* + * linux_elf_load_psection(): + * + * Load a psection at the appropriate address + */ +static void +linux_elf_load_psection(vcset, vp, ph, addr, size, prot) + struct exec_vmcmd_set *vcset; + struct vnode *vp; + Elf32_Phdr *ph; + u_long *addr; + u_long *size; + int *prot; +{ + u_long uaddr; + long diff; + long offset; + u_long msize; + + /* + * If the user specified an address, then we load there. + */ + if (*addr != ~0) { + uaddr = *addr + ph->p_align; + *addr = LINUX_ELF_ALIGN(uaddr, ph->p_align); + uaddr = LINUX_ELF_ALIGN(ph->p_vaddr, ph->p_align); + diff = ph->p_vaddr - uaddr; + } else { + uaddr = ph->p_vaddr; + *addr = LINUX_ELF_ALIGN(uaddr, ph->p_align); + diff = uaddr - *addr; + } + + *prot |= (ph->p_flags & Elf32_pf_r) ? VM_PROT_READ : 0; + *prot |= (ph->p_flags & Elf32_pf_w) ? VM_PROT_WRITE : 0; + *prot |= (ph->p_flags & Elf32_pf_x) ? VM_PROT_EXECUTE : 0; + + offset = ph->p_offset - diff; + *size = ph->p_filesz + diff; + msize = ph->p_memsz + diff; + + DPRINTF(("Elf Seg@ 0x%x/0x%x sz %d/%d off 0x%x/0x%x[%d] algn 0x%x\n", + ph->p_vaddr, *addr, *size, msize, ph->p_offset, offset, + diff, ph->p_align)); + + NEW_VMCMD(vcset, vmcmd_map_readvn, *size, + *addr, vp, offset, *prot); + + /* + * Check if we need to extend the size of the segment + */ + { + u_long rm = round_page(*addr + msize); + u_long rf = round_page(*addr + *size); + if (rm != rf) { + DPRINTF(("zeropad 0x%x-0x%x\n", rf, rm)); + NEW_VMCMD(vcset, vmcmd_map_zero, rm - rf, + rf, NULLVP, 0, *prot); + *size = msize; + } + } +} + + +/* + * linux_elf_set_segment(): + * + * Decide if the segment is text or data, depending on the protection + * and set it appropriately + */ +static int +linux_elf_set_segment(epp, vaddr, size, prot) + struct exec_package *epp; + u_long vaddr; + u_long size; + int prot; +{ + /* + * Kludge: Unfortunately the current implementation of + * exec package assumes a single text and data segment. + * In Elf we can have more, but here we limit ourselves + * to two and hope :-( + * We also assume that the text is r-x, and data is rwx. + */ + switch (prot) { + case (VM_PROT_READ | VM_PROT_EXECUTE): + if (epp->ep_tsize != ~0) { + DPRINTF(("More than one text segment\n")); + return ENOEXEC; + } + epp->ep_taddr = vaddr; + epp->ep_tsize = size; + DPRINTF(("Elf Text@ 0x%x, size %d\n", vaddr, size)); + break; + + case (VM_PROT_READ | VM_PROT_WRITE): + case (VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE): + if (epp->ep_dsize != ~0) { + DPRINTF(("More than one data segment\n")); + return ENOEXEC; + } + epp->ep_daddr = vaddr; + epp->ep_dsize = size; + + DPRINTF(("Elf Data@ 0x%x, size %d\n", vaddr, size)); + break; + + default: + DPRINTF(("Bad protection 0%o\n", prot)); + return ENOEXEC; + } + return 0; +} + + +/* + * linux_elf_read_from(): + * + * Read from vnode into buffer at offset. + */ +static int +linux_elf_read_from(vp, off, p, buf, size) + struct vnode *vp; + u_long off; + struct proc *p; + caddr_t buf; + int size; +{ + int error; + int resid; + + DPRINTF(("read from 0x%x to 0x%x size %d\n", + off, buf, size)); + if ((error = vn_rdwr(UIO_READ, vp, buf, size, + off, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, + &resid, p)) != 0) { + DPRINTF(("Bad read error %d\n", error)); + return error; + } + /* + * See if we got all of it + */ + if (resid != 0) { + DPRINTF(("Incomplete read for header ask=%d, rem=%d\n", + size, resid)); + return error; + } + return 0; +} + + +/* + * linux_elf_load_file(): + * + * Load a file (interpreter/library) pointed to by path + * [stolen from coff_load_shlib()]. Made slightly more generic than + * the svr4 version, for possible later use in linux_uselib(). + */ +static int +linux_elf_load_file(p, path, vcset, entry, ap, last) + struct proc *p; + char *path; + struct exec_vmcmd_set *vcset; + u_long *entry; + struct elf_args *ap; + u_long *last; +{ + int error, i; + struct nameidata nd; + Elf32_Ehdr eh; + Elf32_Phdr *ph = NULL; + u_long phsize; + char *bp = NULL; + u_long addr = *last; + + DPRINTF(("Loading file %s @ %x\n", path, addr)); + + if ((error = linux_emul_find(p, NULL, linux_emul_path, path, &bp, 0)) != 0) + bp = NULL; + else + path = bp; + /* + * 1. open file + * 2. read filehdr + * 3. map text, data, and bss out of it using VM_* + */ + NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, p); + /* first get the vnode */ + if ((error = namei(&nd)) != 0) { + if (bp != NULL) + free((char *) bp, M_TEMP); + return error; + } + if ((error = linux_elf_read_from(nd.ni_vp, 0, p, (caddr_t) &eh, + sizeof(eh))) != 0) + goto bad; + +#ifdef DEBUG_EXEC_LINUX_ELF + print_Ehdr(&eh); +#endif + + if ((error = linux_elf_check_header(&eh, Elf32_et_dyn)) != 0) + goto bad; + + phsize = eh.e_phnum * sizeof(Elf32_Phdr); + ph = (Elf32_Phdr *) malloc(phsize, M_TEMP, M_WAITOK); + + if ((error = linux_elf_read_from(nd.ni_vp, eh.e_phoff, p, + (caddr_t) ph, phsize)) != 0) + goto bad; + + /* + * Load all the necessary sections + */ + for (i = 0; i < eh.e_phnum; i++) { + u_long size = 0; + int prot = 0; +#ifdef DEBUG_EXEC_LINUX_ELF + print_Phdr(&ph[i]); +#endif + + switch (ph[i].p_type) { + case Elf32_pt_load: + linux_elf_load_psection(vcset, nd.ni_vp, &ph[i], &addr, + &size, &prot); + /* Assume that the text segment is r-x only */ + if ((prot & PROT_WRITE) == 0) { + *entry = addr + eh.e_entry; + ap->arg_interp = addr; + DPRINTF(("Interpreter@ 0x%x\n", addr)); + } + addr += size; + break; + + case Elf32_pt_dynamic: + case Elf32_pt_phdr: + case Elf32_pt_note: + break; + + default: + DPRINTF(("interp: Unexpected program header type %d\n", + ph[i].p_type)); + break; + } + } + +bad: + if (ph != NULL) + free((char *) ph, M_TEMP); + if (bp != NULL) + free((char *) bp, M_TEMP); + + *last = addr; + vrele(nd.ni_vp); + return error; +} + + +/* + * exec_linux_elf_makecmds(): Prepare an Elf binary's exec package + * + * First, set of the various offsets/lengths in the exec package. + * + * Then, mark the text image busy (so it can be demand paged) or error + * out if this is not possible. Finally, set up vmcmds for the + * text, data, bss, and stack segments. + */ +int +exec_linux_elf_makecmds(p, epp) + struct proc *p; + struct exec_package *epp; +{ + Elf32_Ehdr *eh = epp->ep_hdr; + Elf32_Phdr *ph, *pp; + int error; + int i; + char interp[MAXPATHLEN]; + u_long pos = 0; + u_long phsize; + +#ifdef DEBUG_EXEC_LINUX_ELF + print_Ehdr(eh); +#endif + if (epp->ep_hdrvalid < sizeof(Elf32_Ehdr)) + return ENOEXEC; + + if (linux_elf_check_header(eh, Elf32_et_exec)) + return ENOEXEC; + + /* + * check if vnode is in open for writing, because we want to + * demand-page out of it. if it is, don't do it, for various + * reasons + */ + if (epp->ep_vp->v_writecount != 0) { +#ifdef DIAGNOSTIC + if (epp->ep_vp->v_flag & VTEXT) + panic("exec: a VTEXT vnode has writecount != 0\n"); +#endif + return ETXTBSY; + } + /* + * Allocate space to hold all the program headers, and read them + * from the file + */ + phsize = eh->e_phnum * sizeof(Elf32_Phdr); + ph = (Elf32_Phdr *) malloc(phsize, M_TEMP, M_WAITOK); + + if ((error = linux_elf_read_from(epp->ep_vp, eh->e_phoff, p, + (caddr_t) ph, phsize)) != 0) + goto bad; + + epp->ep_tsize = ~0; + epp->ep_dsize = ~0; + + interp[0] = '\0'; + + /* + * Load all the necessary sections + */ + for (i = 0; i < eh->e_phnum; i++) { + u_long addr = ~0, size = 0; + int prot = 0; + + pp = &ph[i]; +#ifdef DEBUG_EXEC_LINUX_ELF + print_Phdr(pp); +#endif + + switch (ph[i].p_type) { + case Elf32_pt_load: + linux_elf_load_psection(&epp->ep_vmcmds, epp->ep_vp, + &ph[i], &addr, &size, &prot); + if ((error = linux_elf_set_segment(epp, addr, size, + prot)) != 0) + goto bad; + break; + + case Elf32_pt_shlib: + DPRINTF(("No support for COFF libraries (yet)\n")); + error = ENOEXEC; + goto bad; + + case Elf32_pt_interp: + if (pp->p_filesz >= sizeof(interp)) { + DPRINTF(("Interpreter path too long %d\n", + pp->p_filesz)); + goto bad; + } + if ((error = linux_elf_read_from(epp->ep_vp, pp->p_offset, p, + (caddr_t) interp, pp->p_filesz)) != 0) + goto bad; + break; + + case Elf32_pt_dynamic: + case Elf32_pt_phdr: + case Elf32_pt_note: + break; + + default: + /* + * Not fatal, we don't need to understand everything + * :-) + */ + DPRINTF(("Unsupported program header type %d\n", + pp->p_type)); + break; + } + } + + /* + * Check if we found a dynamically linked binary and arrange to load + * it's interpreter + */ + if (interp[0]) { + struct elf_args *ap; + pos = ~0; + + ap = (struct elf_args *) malloc(sizeof(struct elf_args), + M_TEMP, M_WAITOK); + if ((error = linux_elf_load_file(p, interp, &epp->ep_vmcmds, + &epp->ep_entry, ap, &pos)) != 0) { + free((char *) ap, M_TEMP); + goto bad; + } + /* Arrange to load the program headers. */ + pos = LINUX_ELF_ALIGN(pos + NBPG, NBPG); + DPRINTF(("Program header @0x%x\n", pos)); + ap->arg_phaddr = pos; + NEW_VMCMD(&epp->ep_vmcmds, vmcmd_map_readvn, phsize, + pos, epp->ep_vp, eh->e_phoff, + VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE); + pos += phsize; + + ap->arg_phentsize = eh->e_phentsize; + ap->arg_phnum = eh->e_phnum; + ap->arg_entry = eh->e_entry; + + epp->ep_emul_arg = ap; + } else + epp->ep_entry = eh->e_entry; + + DPRINTF(("taddr 0x%x tsize 0x%x daddr 0x%x dsize 0x%x\n", + epp->ep_taddr, epp->ep_tsize, epp->ep_daddr, epp->ep_dsize)); + + free((char *) ph, M_TEMP); + + DPRINTF(("Elf entry@ 0x%x\n", epp->ep_entry)); + epp->ep_vp->v_flag |= VTEXT; + + epp->ep_emul = &emul_linux_elf; + + return exec_aout_setup_stack(p, epp); + +bad: + free((char *) ph, M_TEMP); + kill_vmcmds(&epp->ep_vmcmds); + return ENOEXEC; +} +/* + * The Linux system call to load shared libraries, a.out version. The + * a.out shared libs are just files that are mapped onto a fixed + * address in the process' address space. The address is given in * a_entry. Read in the header, set up some VM commands and run them. * * Yes, both text and data are mapped at once, so we're left with @@ -357,7 +995,7 @@ exec_linux_aout_prep_qmagic(p, epp) * Yuck. * * Because of the problem with ZMAGIC executables (text starts - * at 0x400 in the file, but needs t be mapped at 0), ZMAGIC + * at 0x400 in the file, but needs to be mapped at 0), ZMAGIC * shared libs are not handled very efficiently :-( */ @@ -399,6 +1037,9 @@ linux_uselib(p, uap, retval) return ENOEXEC; } + if (LINUX_N_MACHTYPE(&hdr) != LINUX_MID_MACHINE) + return ENOEXEC; + magic = LINUX_N_MAGIC(&hdr); taddr = hdr.a_entry & (~(NBPG - 1)); tsize = hdr.a_text; diff --git a/sys/compat/linux/linux_exec.h b/sys/compat/linux/linux_exec.h index 4bd97aeb20cd..4645b901a74c 100644 --- a/sys/compat/linux/linux_exec.h +++ b/sys/compat/linux/linux_exec.h @@ -1,4 +1,4 @@ -/* $NetBSD: linux_exec.h,v 1.2 1995/04/07 22:23:26 fvdl Exp $ */ +/* $NetBSD: linux_exec.h,v 1.3 1995/06/11 14:56:56 fvdl Exp $ */ /* * Copyright (c) 1995 Frank van der Linden @@ -35,6 +35,12 @@ #ifndef _LINUX_EXEC_H #define _LINUX_EXEC_H +/* + * Include this for ELF definitions. Should be an emul-independent file + * someday. + */ +#include + #define LINUX_M_I386 100 /* Sparc? Alpha? */ @@ -66,6 +72,7 @@ #define LINUX_N_BSSADDR(x,m) (LINUX_N_DATADDR(x,m) + (x).a_data) int exec_linux_aout_makecmds __P((struct proc *, struct exec_package *)); +int exec_linux_elf_makecmds __P((struct proc *, struct exec_package *)); extern char linux_sigcode[], linux_esigcode[]; diff --git a/sys/compat/linux/linux_misc.c b/sys/compat/linux/linux_misc.c index 9109c6496cad..dceb119675de 100644 --- a/sys/compat/linux/linux_misc.c +++ b/sys/compat/linux/linux_misc.c @@ -1,4 +1,4 @@ -/* $NetBSD: linux_misc.c,v 1.5 1995/06/10 22:19:14 mycroft Exp $ */ +/* $NetBSD: linux_misc.c,v 1.6 1995/06/11 14:56:59 fvdl Exp $ */ /* * Copyright (c) 1995 Frank van der Linden @@ -789,3 +789,23 @@ linux_getpgid(p, uap, retval) retval[0] = targp->p_pgid; return 0; } + +/* + * Set the 'personality' (emulation mode) for the current process. Only + * accept the Linux personality here (0). This call is needed because + * the Linux ELF crt0 issues it in an ugly kludge to make sure that + * ELF binaries run in Linux mode, not SVR4 mode. + */ +int +linux_personality(p, uap, retval) + struct proc *p; + struct linux_personality_args /* P + syscallarg(int) per; + } */ *uap; + register_t *retval; +{ + if (SCARG(uap, per) != 0) + return EINVAL; + retval[0] = 0; + return 0; +} diff --git a/sys/compat/linux/multiarch/linux_break.c b/sys/compat/linux/multiarch/linux_break.c index 0bc1348a01b4..8b3d5a746e13 100644 --- a/sys/compat/linux/multiarch/linux_break.c +++ b/sys/compat/linux/multiarch/linux_break.c @@ -1,4 +1,4 @@ -/* $NetBSD: linux_break.c,v 1.5 1995/06/10 22:19:14 mycroft Exp $ */ +/* $NetBSD: linux_break.c,v 1.6 1995/06/11 14:56:59 fvdl Exp $ */ /* * Copyright (c) 1995 Frank van der Linden @@ -789,3 +789,23 @@ linux_getpgid(p, uap, retval) retval[0] = targp->p_pgid; return 0; } + +/* + * Set the 'personality' (emulation mode) for the current process. Only + * accept the Linux personality here (0). This call is needed because + * the Linux ELF crt0 issues it in an ugly kludge to make sure that + * ELF binaries run in Linux mode, not SVR4 mode. + */ +int +linux_personality(p, uap, retval) + struct proc *p; + struct linux_personality_args /* P + syscallarg(int) per; + } */ *uap; + register_t *retval; +{ + if (SCARG(uap, per) != 0) + return EINVAL; + retval[0] = 0; + return 0; +} diff --git a/sys/compat/linux/multiarch/linux_misc_notalpha.c b/sys/compat/linux/multiarch/linux_misc_notalpha.c index 11710e762f53..386297902488 100644 --- a/sys/compat/linux/multiarch/linux_misc_notalpha.c +++ b/sys/compat/linux/multiarch/linux_misc_notalpha.c @@ -1,4 +1,4 @@ -/* $NetBSD: linux_misc_notalpha.c,v 1.5 1995/06/10 22:19:14 mycroft Exp $ */ +/* $NetBSD: linux_misc_notalpha.c,v 1.6 1995/06/11 14:56:59 fvdl Exp $ */ /* * Copyright (c) 1995 Frank van der Linden @@ -789,3 +789,23 @@ linux_getpgid(p, uap, retval) retval[0] = targp->p_pgid; return 0; } + +/* + * Set the 'personality' (emulation mode) for the current process. Only + * accept the Linux personality here (0). This call is needed because + * the Linux ELF crt0 issues it in an ugly kludge to make sure that + * ELF binaries run in Linux mode, not SVR4 mode. + */ +int +linux_personality(p, uap, retval) + struct proc *p; + struct linux_personality_args /* P + syscallarg(int) per; + } */ *uap; + register_t *retval; +{ + if (SCARG(uap, per) != 0) + return EINVAL; + retval[0] = 0; + return 0; +} diff --git a/sys/compat/linux/multiarch/linux_oldmmap.c b/sys/compat/linux/multiarch/linux_oldmmap.c index ee5f4fb0c6f8..de564fc3732d 100644 --- a/sys/compat/linux/multiarch/linux_oldmmap.c +++ b/sys/compat/linux/multiarch/linux_oldmmap.c @@ -1,4 +1,4 @@ -/* $NetBSD: linux_oldmmap.c,v 1.5 1995/06/10 22:19:14 mycroft Exp $ */ +/* $NetBSD: linux_oldmmap.c,v 1.6 1995/06/11 14:56:59 fvdl Exp $ */ /* * Copyright (c) 1995 Frank van der Linden @@ -789,3 +789,23 @@ linux_getpgid(p, uap, retval) retval[0] = targp->p_pgid; return 0; } + +/* + * Set the 'personality' (emulation mode) for the current process. Only + * accept the Linux personality here (0). This call is needed because + * the Linux ELF crt0 issues it in an ugly kludge to make sure that + * ELF binaries run in Linux mode, not SVR4 mode. + */ +int +linux_personality(p, uap, retval) + struct proc *p; + struct linux_personality_args /* P + syscallarg(int) per; + } */ *uap; + register_t *retval; +{ + if (SCARG(uap, per) != 0) + return EINVAL; + retval[0] = 0; + return 0; +} diff --git a/sys/compat/linux/multiarch/linux_oldolduname.c b/sys/compat/linux/multiarch/linux_oldolduname.c index 74e2c43b5bbd..c86a02fb30ba 100644 --- a/sys/compat/linux/multiarch/linux_oldolduname.c +++ b/sys/compat/linux/multiarch/linux_oldolduname.c @@ -1,4 +1,4 @@ -/* $NetBSD: linux_oldolduname.c,v 1.5 1995/06/10 22:19:14 mycroft Exp $ */ +/* $NetBSD: linux_oldolduname.c,v 1.6 1995/06/11 14:56:59 fvdl Exp $ */ /* * Copyright (c) 1995 Frank van der Linden @@ -789,3 +789,23 @@ linux_getpgid(p, uap, retval) retval[0] = targp->p_pgid; return 0; } + +/* + * Set the 'personality' (emulation mode) for the current process. Only + * accept the Linux personality here (0). This call is needed because + * the Linux ELF crt0 issues it in an ugly kludge to make sure that + * ELF binaries run in Linux mode, not SVR4 mode. + */ +int +linux_personality(p, uap, retval) + struct proc *p; + struct linux_personality_args /* P + syscallarg(int) per; + } */ *uap; + register_t *retval; +{ + if (SCARG(uap, per) != 0) + return EINVAL; + retval[0] = 0; + return 0; +} diff --git a/sys/compat/linux/multiarch/linux_oldselect.c b/sys/compat/linux/multiarch/linux_oldselect.c index 0850965ed315..29539cd62841 100644 --- a/sys/compat/linux/multiarch/linux_oldselect.c +++ b/sys/compat/linux/multiarch/linux_oldselect.c @@ -1,4 +1,4 @@ -/* $NetBSD: linux_oldselect.c,v 1.5 1995/06/10 22:19:14 mycroft Exp $ */ +/* $NetBSD: linux_oldselect.c,v 1.6 1995/06/11 14:56:59 fvdl Exp $ */ /* * Copyright (c) 1995 Frank van der Linden @@ -789,3 +789,23 @@ linux_getpgid(p, uap, retval) retval[0] = targp->p_pgid; return 0; } + +/* + * Set the 'personality' (emulation mode) for the current process. Only + * accept the Linux personality here (0). This call is needed because + * the Linux ELF crt0 issues it in an ugly kludge to make sure that + * ELF binaries run in Linux mode, not SVR4 mode. + */ +int +linux_personality(p, uap, retval) + struct proc *p; + struct linux_personality_args /* P + syscallarg(int) per; + } */ *uap; + register_t *retval; +{ + if (SCARG(uap, per) != 0) + return EINVAL; + retval[0] = 0; + return 0; +} diff --git a/sys/compat/linux/multiarch/linux_olduname.c b/sys/compat/linux/multiarch/linux_olduname.c index 20b3e371f766..2ab31c99c303 100644 --- a/sys/compat/linux/multiarch/linux_olduname.c +++ b/sys/compat/linux/multiarch/linux_olduname.c @@ -1,4 +1,4 @@ -/* $NetBSD: linux_olduname.c,v 1.5 1995/06/10 22:19:14 mycroft Exp $ */ +/* $NetBSD: linux_olduname.c,v 1.6 1995/06/11 14:56:59 fvdl Exp $ */ /* * Copyright (c) 1995 Frank van der Linden @@ -789,3 +789,23 @@ linux_getpgid(p, uap, retval) retval[0] = targp->p_pgid; return 0; } + +/* + * Set the 'personality' (emulation mode) for the current process. Only + * accept the Linux personality here (0). This call is needed because + * the Linux ELF crt0 issues it in an ugly kludge to make sure that + * ELF binaries run in Linux mode, not SVR4 mode. + */ +int +linux_personality(p, uap, retval) + struct proc *p; + struct linux_personality_args /* P + syscallarg(int) per; + } */ *uap; + register_t *retval; +{ + if (SCARG(uap, per) != 0) + return EINVAL; + retval[0] = 0; + return 0; +} diff --git a/sys/compat/linux/multiarch/linux_pipe.c b/sys/compat/linux/multiarch/linux_pipe.c index 8a3bdd880155..978f63edb30b 100644 --- a/sys/compat/linux/multiarch/linux_pipe.c +++ b/sys/compat/linux/multiarch/linux_pipe.c @@ -1,4 +1,4 @@ -/* $NetBSD: linux_pipe.c,v 1.5 1995/06/10 22:19:14 mycroft Exp $ */ +/* $NetBSD: linux_pipe.c,v 1.6 1995/06/11 14:56:59 fvdl Exp $ */ /* * Copyright (c) 1995 Frank van der Linden @@ -789,3 +789,23 @@ linux_getpgid(p, uap, retval) retval[0] = targp->p_pgid; return 0; } + +/* + * Set the 'personality' (emulation mode) for the current process. Only + * accept the Linux personality here (0). This call is needed because + * the Linux ELF crt0 issues it in an ugly kludge to make sure that + * ELF binaries run in Linux mode, not SVR4 mode. + */ +int +linux_personality(p, uap, retval) + struct proc *p; + struct linux_personality_args /* P + syscallarg(int) per; + } */ *uap; + register_t *retval; +{ + if (SCARG(uap, per) != 0) + return EINVAL; + retval[0] = 0; + return 0; +} diff --git a/sys/compat/linux/syscalls.master b/sys/compat/linux/syscalls.master index 609123ba74bf..cd4bca11b659 100644 --- a/sys/compat/linux/syscalls.master +++ b/sys/compat/linux/syscalls.master @@ -1,4 +1,4 @@ - $NetBSD: syscalls.master,v 1.5 1995/05/06 18:16:40 mycroft Exp $ + $NetBSD: syscalls.master,v 1.6 1995/06/11 14:57:01 fvdl Exp $ ; @(#)syscalls.master 8.1 (Berkeley) 7/19/93 @@ -194,7 +194,7 @@ 133 NOARGS { int fchdir(int fd); } 134 UNIMPL linux_bdflush 135 UNIMPL linux_sysfs -136 UNIMPL linux_personality +136 STD { int linux_personality(int per); } 137 UNIMPL linux_afs_syscall 138 UNIMPL linux_setfsuid 139 UNIMPL linux_getfsuid