/* * Look for a .gnu_debuglink, specific to x86_64 interpeter */ int ELFNAME2(linux,debuglink_signature)(struct lwp *l, struct exec_package *epp, Elf_Ehdr *eh) { size_t shsize; int strndx; size_t i; static const char signature[] = ".gnu_debuglink"; char *strtable = NULL; Elf_Shdr *sh; int error; /* * load the section header table */ shsize = eh->e_shnum * sizeof(Elf_Shdr); sh = (Elf_Shdr *) malloc(shsize, M_TEMP, M_WAITOK); error = exec_read_from(l, epp->ep_vp, eh->e_shoff, sh, shsize); if (error) goto out; /* * Now let's find the string table. If it does not exists, give up. */ strndx = (int)(eh->e_shstrndx); if (strndx == SHN_UNDEF) { error = ENOEXEC; goto out; } /* * strndx is the index in section header table of the string table * section get the whole string table in strtable, and then we get access to the names * s->sh_name is the offset of the section name in strtable. */ strtable = malloc(sh[strndx].sh_size, M_TEMP, M_WAITOK); error = exec_read_from(l, epp->ep_vp, sh[strndx].sh_offset, strtable, sh[strndx].sh_size); if (error) goto out; for (i = 0; i < eh->e_shnum; i++) { Elf_Shdr *s = &sh[i]; if (!memcmp((void*)(&(strtable[s->sh_name])), signature, sizeof(signature))) { DPRINTF(("linux_debuglink_sig=%s\n", &(strtable[s->sh_name]))); error = 0; goto out; } } error = ENOEXEC; out: free(sh, M_TEMP); if (strtable) free(strtable, M_TEMP); return (error); }
/* * Take advantage of the fact that all the linux binaries are compiled * with gcc, and gcc sticks in the comment field a signature. Note that * on SVR4 binaries, the gcc signature will follow the OS name signature, * that will not be a problem. We don't bother to read in the string table, * but we check all the progbits headers. * * XXX This only works in the i386. On the alpha (at least) * XXX we have the same gcc signature which incorrectly identifies * XXX NetBSD binaries as Linux. */ int ELFNAME2(linux,gcc_signature)( struct lwp *l, struct exec_package *epp, Elf_Ehdr *eh) { size_t shsize; size_t i; static const char signature[] = "\0GCC: (GNU) "; char tbuf[sizeof(signature) - 1]; Elf_Shdr *sh; int error; shsize = eh->e_shnum * sizeof(Elf_Shdr); sh = (Elf_Shdr *) malloc(shsize, M_TEMP, M_WAITOK); error = exec_read_from(l, epp->ep_vp, eh->e_shoff, sh, shsize); if (error) goto out; for (i = 0; i < eh->e_shnum; i++) { Elf_Shdr *s = &sh[i]; /* * Identify candidates for the comment header; * Header cannot have a load address, or flags and * it must be large enough. */ if (s->sh_type != SHT_PROGBITS || s->sh_addr != 0 || s->sh_flags != 0 || s->sh_size < sizeof(signature) - 1) continue; error = exec_read_from(l, epp->ep_vp, s->sh_offset, tbuf, sizeof(signature) - 1); if (error) continue; /* * error is 0, if the signatures match we are done. */ DPRINTF(("linux_gcc_sig: sig=%s\n", tbuf)); if (!memcmp(tbuf, signature, sizeof(signature) - 1)) { error = 0; goto out; } } error = ENOEXEC; out: free(sh, M_TEMP); return (error); }
/* * Check PE signature. */ int pecoff_signature(struct lwp *l, struct vnode *vp, struct pecoff_dos_filehdr *dp) { int error; char tbuf[sizeof(signature) - 1]; if (DOS_BADMAG(dp)) { return ENOEXEC; } error = exec_read_from(l, vp, dp->d_peofs, tbuf, sizeof(tbuf)); if (error) { return error; } if (memcmp(tbuf, signature, sizeof(signature) - 1) == 0) { return 0; } return EFTYPE; }
int exec_pecoff_makecmds(struct lwp *l, struct exec_package *epp) { int error, peofs; struct pecoff_dos_filehdr *dp = epp->ep_hdr; struct coff_filehdr *fp; struct proc *p; p = l->l_proc; /* * mmap EXE file (PE format) * 1. read header (DOS,PE) * 2. mmap code section (READ|EXEC) * 3. mmap other section, such as data (READ|WRITE|EXEC) */ if (epp->ep_hdrvalid < PECOFF_DOS_HDR_SIZE) { return ENOEXEC; } if ((error = pecoff_signature(l, epp->ep_vp, dp)) != 0) return error; if ((error = vn_marktext(epp->ep_vp)) != 0) return error; peofs = dp->d_peofs + sizeof(signature) - 1; fp = malloc(PECOFF_HDR_SIZE, M_TEMP, M_WAITOK); error = exec_read_from(l, epp->ep_vp, peofs, fp, PECOFF_HDR_SIZE); if (error) { free(fp, M_TEMP); return error; } error = exec_pecoff_coff_makecmds(l, epp, fp, peofs); if (error != 0) kill_vmcmds(&epp->ep_vmcmds); free(fp, M_TEMP); return error; }
int ELFNAME2(linux,copyargs)(struct lwp *l, struct exec_package *pack, struct ps_strings *arginfo, char **stackp, void *argp) { struct linux_extra_stack_data64 *esdp, esd; struct elf_args *ap; struct vattr *vap; Elf_Ehdr *eh; Elf_Phdr *ph; u_long phsize; Elf_Addr phdr = 0; int error; int i; if ((error = copyargs(l, pack, arginfo, stackp, argp)) != 0) return error; /* * Push extra arguments on the stack needed by dynamically * linked binaries and static binaries as well. */ memset(&esd, 0, sizeof(esd)); esdp = (struct linux_extra_stack_data64 *)(*stackp); ap = (struct elf_args *)pack->ep_emul_arg; vap = pack->ep_vap; eh = (Elf_Ehdr *)pack->ep_hdr; /* * We forgot this, so we need to reload it now. XXX keep track of it? */ if (ap == NULL) { phsize = eh->e_phnum * sizeof(Elf_Phdr); ph = (Elf_Phdr *)kmem_alloc(phsize, KM_SLEEP); error = exec_read_from(l, pack->ep_vp, eh->e_phoff, ph, phsize); if (error != 0) { for (i = 0; i < eh->e_phnum; i++) { if (ph[i].p_type == PT_PHDR) { phdr = ph[i].p_vaddr; break; } } } kmem_free(ph, phsize); } /* * The exec_package doesn't have a proc pointer and it's not * exactly trivial to add one since the credentials are * changing. XXX Linux uses curlwp's credentials. * Why can't we use them too? */ i = 0; esd.ai[i].a_type = LINUX_AT_HWCAP; esd.ai[i++].a_v = rcr4(); esd.ai[i].a_type = AT_PAGESZ; esd.ai[i++].a_v = PAGE_SIZE; esd.ai[i].a_type = LINUX_AT_CLKTCK; esd.ai[i++].a_v = hz; esd.ai[i].a_type = AT_PHDR; esd.ai[i++].a_v = (ap ? ap->arg_phaddr: phdr); esd.ai[i].a_type = AT_PHENT; esd.ai[i++].a_v = (ap ? ap->arg_phentsize : eh->e_phentsize); esd.ai[i].a_type = AT_PHNUM; esd.ai[i++].a_v = (ap ? ap->arg_phnum : eh->e_phnum); esd.ai[i].a_type = AT_BASE; esd.ai[i++].a_v = (ap ? ap->arg_interp : 0); esd.ai[i].a_type = AT_FLAGS; esd.ai[i++].a_v = 0; esd.ai[i].a_type = AT_ENTRY; esd.ai[i++].a_v = (ap ? ap->arg_entry : eh->e_entry); esd.ai[i].a_type = LINUX_AT_EGID; esd.ai[i++].a_v = ((vap->va_mode & S_ISGID) ? vap->va_gid : kauth_cred_getegid(l->l_cred)); esd.ai[i].a_type = LINUX_AT_GID; esd.ai[i++].a_v = kauth_cred_getgid(l->l_cred); esd.ai[i].a_type = LINUX_AT_EUID; esd.ai[i++].a_v = ((vap->va_mode & S_ISUID) ? vap->va_uid : kauth_cred_geteuid(l->l_cred)); esd.ai[i].a_type = LINUX_AT_UID; esd.ai[i++].a_v = kauth_cred_getuid(l->l_cred); esd.ai[i].a_type = LINUX_AT_SECURE; esd.ai[i++].a_v = 0; esd.ai[i].a_type = LINUX_AT_PLATFORM; esd.ai[i++].a_v = (Elf_Addr)&esdp->hw_platform[0]; esd.ai[i].a_type = AT_NULL; esd.ai[i++].a_v = 0; #ifdef DEBUG_LINUX if (i != LINUX_ELF_AUX_ENTRIES) { printf("linux_elf64_copyargs: %d Aux entries\n", i); return EINVAL; } #endif strcpy(esd.hw_platform, LINUX_PLATFORM); exec_free_emul_arg(pack); /* * Copy out the ELF auxiliary table and hw platform name */ if ((error = copyout(&esd, esdp, sizeof(esd))) != 0) return error; *stackp += sizeof(esd); return 0; }
int exec_pecoff_prep_zmagic(struct lwp *l, struct exec_package *epp, struct coff_filehdr *fp, struct coff_aouthdr *ap, int peofs) { int error, i; struct pecoff_opthdr *wp; long daddr, baddr, bsize; u_long tsize, dsize; struct coff_scnhdr *sh; struct pecoff_args *argp; int scnsiz = sizeof(struct coff_scnhdr) * fp->f_nscns; wp = (void *)((char *)ap + sizeof(struct coff_aouthdr)); epp->ep_tsize = ap->a_tsize; epp->ep_daddr = VM_MAXUSER_ADDRESS; epp->ep_dsize = 0; /* read section header */ sh = malloc(scnsiz, M_TEMP, M_WAITOK); error = exec_read_from(l, epp->ep_vp, peofs + PECOFF_HDR_SIZE, sh, scnsiz); if (error) { free(sh, M_TEMP); return error; } /* * map section */ for (i = 0; i < fp->f_nscns; i++) { int prot = /*0*/VM_PROT_WRITE; long s_flags = sh[i].s_flags; if ((s_flags & COFF_STYP_DISCARD) != 0) continue; sh[i].s_vaddr += wp->w_base; /* RVA --> VA */ if ((s_flags & COFF_STYP_TEXT) != 0) { /* set up command for text segment */ /* DPRINTF(("COFF text addr %lx size %ld offset %ld\n", sh[i].s_vaddr, sh[i].s_size, sh[i].s_scnptr)); */ pecoff_load_section(&epp->ep_vmcmds, epp->ep_vp, &sh[i], (long *)&epp->ep_taddr, &tsize, &prot); } else if ((s_flags & COFF_STYP_BSS) != 0) { /* set up command for bss segment */ baddr = sh[i].s_vaddr; bsize = sh[i].s_paddr; if (bsize) NEW_VMCMD(&epp->ep_vmcmds, vmcmd_map_zero, bsize, baddr, NULLVP, 0, VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE); epp->ep_daddr = min(epp->ep_daddr, baddr); bsize = baddr + bsize - epp->ep_daddr; epp->ep_dsize = max(epp->ep_dsize, bsize); } else if ((s_flags & (COFF_STYP_DATA|COFF_STYP_READ)) != 0) { /* set up command for data segment */ /* DPRINTF(("COFF data addr %lx size %ld offset %ld\n", sh[i].s_vaddr, sh[i].s_size, sh[i].s_scnptr));*/ pecoff_load_section(&epp->ep_vmcmds, epp->ep_vp, &sh[i], &daddr, &dsize, &prot); epp->ep_daddr = min(epp->ep_daddr, daddr); dsize = daddr + dsize - epp->ep_daddr; epp->ep_dsize = max(epp->ep_dsize, dsize); } } /* set up ep_emul_arg */ argp = malloc(sizeof(struct pecoff_args), M_TEMP, M_WAITOK); epp->ep_emul_arg = argp; argp->a_abiversion = NETBSDPE_ABI_VERSION; argp->a_zero = 0; argp->a_entry = wp->w_base + ap->a_entry; argp->a_end = epp->ep_daddr + epp->ep_dsize; argp->a_opthdr = *wp; /* * load dynamic linker */ error = pecoff_load_file(l, epp, "/usr/libexec/ld.so.dll", &epp->ep_vmcmds, &epp->ep_entry, argp); if (error) { free(sh, M_TEMP); return error; } #if 0 DPRINTF(("text addr: %lx size: %ld data addr: %lx size: %ld entry: %lx\n", epp->ep_taddr, epp->ep_tsize, epp->ep_daddr, epp->ep_dsize, epp->ep_entry)); #endif free(sh, M_TEMP); return (*epp->ep_esch->es_setup_stack)(l, epp); }
/* * load(mmap) file. for dynamic linker (ld.so.dll) */ int pecoff_load_file(struct lwp *l, struct exec_package *epp, const char *path, struct exec_vmcmd_set *vcset, u_long *entry, struct pecoff_args *argp) { int error, peofs, scnsiz, i; struct vnode *vp; struct vattr attr; struct pecoff_dos_filehdr dh; struct coff_filehdr *fp = 0; struct coff_aouthdr *ap; struct pecoff_opthdr *wp; struct coff_scnhdr *sh = 0; error = emul_find_interp(l, epp, path); if (error != 0) return error; vp = epp->ep_interp; epp->ep_interp = NULL; vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); /* * If it's not marked as executable, or it's not a regular * file, we don't allow it to be used. */ if (vp->v_type != VREG) { error = EACCES; goto badunlock; } if ((error = VOP_ACCESS(vp, VEXEC, l->l_cred)) != 0) goto badunlock; /* get attributes */ if ((error = VOP_GETATTR(vp, &attr, l->l_cred)) != 0) goto badunlock; /* * Check mount point. Though we're not trying to exec this binary, * we will be executing code from it, so if the mount point * disallows execution or set-id-ness, we punt or kill the set-id. */ if (vp->v_mount->mnt_flag & MNT_NOEXEC) { error = EACCES; goto badunlock; } if (vp->v_mount->mnt_flag & MNT_NOSUID) epp->ep_vap->va_mode &= ~(S_ISUID | S_ISGID); if ((error = vn_marktext(vp))) goto badunlock; VOP_UNLOCK(vp, 0); /* * Read header. */ error = exec_read_from(l, vp, 0, &dh, sizeof(dh)); if (error != 0) goto bad; if ((error = pecoff_signature(l, vp, &dh)) != 0) goto bad; fp = malloc(PECOFF_HDR_SIZE, M_TEMP, M_WAITOK); peofs = dh.d_peofs + sizeof(signature) - 1; error = exec_read_from(l, vp, peofs, fp, PECOFF_HDR_SIZE); if (error != 0) goto bad; if (COFF_BADMAG(fp)) { error = ENOEXEC; goto bad; } ap = (void *)((char *)fp + sizeof(struct coff_filehdr)); wp = (void *)((char *)ap + sizeof(struct coff_aouthdr)); /* read section header */ scnsiz = sizeof(struct coff_scnhdr) * fp->f_nscns; sh = malloc(scnsiz, M_TEMP, M_WAITOK); if ((error = exec_read_from(l, vp, peofs + PECOFF_HDR_SIZE, sh, scnsiz)) != 0) goto bad; /* * Read section header, and mmap. */ for (i = 0; i < fp->f_nscns; i++) { int prot = 0; long addr; u_long size; if (sh[i].s_flags & COFF_STYP_DISCARD) continue; /* XXX ? */ if ((sh[i].s_flags & COFF_STYP_TEXT) && (sh[i].s_flags & COFF_STYP_EXEC) == 0) continue; if ((sh[i].s_flags & (COFF_STYP_TEXT|COFF_STYP_DATA| COFF_STYP_BSS|COFF_STYP_READ)) == 0) continue; sh[i].s_vaddr += wp->w_base; /* RVA --> VA */ pecoff_load_section(vcset, vp, &sh[i], &addr, &size, &prot); } *entry = wp->w_base + ap->a_entry; argp->a_ldbase = wp->w_base; argp->a_ldexport = wp->w_imghdr[0].i_vaddr + wp->w_base; free(fp, M_TEMP); free(sh, M_TEMP); /*XXXUNCONST*/ vrele(vp); return 0; badunlock: VOP_UNLOCK(vp, 0); bad: if (fp != 0) free(fp, M_TEMP); if (sh != 0) free(sh, M_TEMP); /*XXXUNCONST*/ vrele(vp); return error; }
int ELFNAME2(linux,signature)(struct lwp *l, struct exec_package *epp, Elf_Ehdr *eh, char *itp) { size_t i; Elf_Phdr *ph; size_t phsize; int error; static const char linux[] = "Linux"; if (eh->e_ident[EI_OSABI] == 3 || memcmp(&eh->e_ident[EI_ABIVERSION], linux, sizeof(linux)) == 0) return 0; phsize = eh->e_phnum * sizeof(Elf_Phdr); ph = (Elf_Phdr *)malloc(phsize, M_TEMP, M_WAITOK); error = exec_read_from(l, epp->ep_vp, eh->e_phoff, ph, phsize); if (error) goto out; for (i = 0; i < eh->e_phnum; i++) { Elf_Phdr *ephp = &ph[i]; Elf_Nhdr *np; u_int32_t *abi; if (ephp->p_type != PT_NOTE || ephp->p_filesz > 1024 || ephp->p_filesz < sizeof(Elf_Nhdr) + 20) continue; np = (Elf_Nhdr *)malloc(ephp->p_filesz, M_TEMP, M_WAITOK); error = exec_read_from(l, epp->ep_vp, ephp->p_offset, np, ephp->p_filesz); if (error) goto next; if (np->n_type != ELF_NOTE_TYPE_ABI_TAG || np->n_namesz != ELF_NOTE_ABI_NAMESZ || np->n_descsz != ELF_NOTE_ABI_DESCSZ || memcmp((void *)(np + 1), ELF_NOTE_ABI_NAME, ELF_NOTE_ABI_NAMESZ)) goto next; /* Make sure the OS is Linux. */ abi = (u_int32_t *)((char *)np + sizeof(Elf_Nhdr) + np->n_namesz); if (abi[0] == ELF_NOTE_ABI_OS_LINUX) error = 0; else error = ENOEXEC; free(np, M_TEMP); goto out; next: free(np, M_TEMP); continue; } /* Check for certain intepreter names. */ if (itp) { if (!strncmp(itp, "/lib/ld-linux", 13) || #if (ELFSIZE == 64) !strncmp(itp, "/lib64/ld-linux", 15) || #endif !strncmp(itp, "/lib/ld.so.", 11)) error = 0; else error = ENOEXEC; goto out; } error = ENOEXEC; out: free(ph, M_TEMP); return (error); }