X-Git-Url: https://gitweb.dragonflybsd.org/dragonfly.git/blobdiff_plain/7295dc4694612795b7574965e1527509404c9c0f..e0e309580f5eade6ef7635e7a33ef9f779bbce47:/sys/kern/imgact_elf.c diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c index 6146fd2b26..4c84950577 100644 --- a/sys/kern/imgact_elf.c +++ b/sys/kern/imgact_elf.c @@ -1,4 +1,5 @@ /*- + * Copyright (c) 2000 David O'Brien * Copyright (c) 1995-1996 Søren Schmidt * Copyright (c) 1996 Peter Wemm * All rights reserved. @@ -27,7 +28,6 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/kern/imgact_elf.c,v 1.73.2.13 2002/12/28 19:49:41 dillon Exp $ - * $DragonFly: src/sys/kern/imgact_elf.c,v 1.55 2008/08/17 17:21:36 nth Exp $ */ #include @@ -68,136 +68,104 @@ #include #include #include -#define OLD_EI_BRAND 8 -__ElfType(Brandinfo); -__ElfType(Auxargs); +#define OLD_EI_BRAND 8 +#define truncps(va,ps) ((va) & ~(ps - 1)) +#define aligned(a,t) (truncps((u_long)(a), sizeof(t)) == (u_long)(a)) -static int elf_check_header (const Elf_Ehdr *hdr); -static int elf_freebsd_fixup (register_t **stack_base, - struct image_params *imgp); -static int elf_load_file (struct proc *p, const char *file, u_long *addr, +static int __elfN(check_header)(const Elf_Ehdr *hdr); +static Elf_Brandinfo *__elfN(get_brandinfo)(struct image_params *imgp, + const char *interp, int32_t *osrel); +static int __elfN(load_file)(struct proc *p, const char *file, u_long *addr, u_long *entry); -static int elf_load_section (struct proc *p, +static int __elfN(load_section)(struct proc *p, struct vmspace *vmspace, struct vnode *vp, vm_offset_t offset, caddr_t vmaddr, size_t memsz, size_t filsz, vm_prot_t prot); -static int exec_elf_imgact (struct image_params *imgp); +static int __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp); +static boolean_t __elfN(bsd_trans_osrel)(const Elf_Note *note, + int32_t *osrel); +static boolean_t __elfN(check_note)(struct image_params *imgp, + Elf_Brandnote *checknote, int32_t *osrel); +static boolean_t check_PT_NOTE(struct image_params *imgp, + Elf_Brandnote *checknote, int32_t *osrel, const Elf_Phdr * pnote); +static boolean_t extract_interpreter(struct image_params *imgp, + const Elf_Phdr *pinterpreter, char *data); -static int elf_trace = 0; -SYSCTL_INT(_debug, OID_AUTO, elf_trace, CTLFLAG_RW, &elf_trace, 0, ""); static int elf_legacy_coredump = 0; -SYSCTL_INT(_debug, OID_AUTO, elf_legacy_coredump, CTLFLAG_RW, - &elf_legacy_coredump, 0, ""); - -static int dragonfly_match_abi_note(const Elf_Note *); -static int freebsd_match_abi_note(const Elf_Note *); - -static struct sysentvec elf_freebsd_sysvec = { - SYS_MAXSYSCALL, - sysent, - -1, - 0, - 0, - 0, - 0, - 0, - elf_freebsd_fixup, - sendsig, - sigcode, - &szsigcode, - 0, +static int __elfN(fallback_brand) = -1; #if defined(__x86_64__) - "FreeBSD ELF64", -#else - "FreeBSD ELF32", +SYSCTL_NODE(_kern, OID_AUTO, elf64, CTLFLAG_RW, 0, ""); +SYSCTL_INT(_debug, OID_AUTO, elf64_legacy_coredump, CTLFLAG_RW, + &elf_legacy_coredump, 0, "legacy coredump mode"); +SYSCTL_INT(_kern_elf64, OID_AUTO, fallback_brand, CTLFLAG_RW, + &elf64_fallback_brand, 0, "ELF64 brand of last resort"); +TUNABLE_INT("kern.elf64.fallback_brand", &elf64_fallback_brand); +#else /* i386 assumed */ +SYSCTL_NODE(_kern, OID_AUTO, elf32, CTLFLAG_RW, 0, ""); +SYSCTL_INT(_debug, OID_AUTO, elf32_legacy_coredump, CTLFLAG_RW, + &elf_legacy_coredump, 0, "legacy coredump mode"); +SYSCTL_INT(_kern_elf32, OID_AUTO, fallback_brand, CTLFLAG_RW, + &elf32_fallback_brand, 0, "ELF32 brand of last resort"); +TUNABLE_INT("kern.elf32.fallback_brand", &elf32_fallback_brand); #endif - elf_coredump, - NULL, - MINSIGSTKSZ -}; -static Elf_Brandinfo freebsd_brand_info = { - ELFOSABI_FREEBSD, - "FreeBSD", - freebsd_match_abi_note, - "", - "/usr/libexec/ld-elf.so.1", - &elf_freebsd_sysvec - }; - -static Elf_Brandinfo dragonfly_brand_info = { - ELFOSABI_NONE, - "DragonFly", - dragonfly_match_abi_note, - "", - "/usr/libexec/ld-elf.so.2", - &elf_freebsd_sysvec - }; - -static Elf_Brandinfo *elf_brand_list[MAX_BRANDS] = { - &dragonfly_brand_info, - &freebsd_brand_info, - NULL, NULL, NULL, - NULL, NULL, NULL - }; +static Elf_Brandinfo *elf_brand_list[MAX_BRANDS]; -static int -freebsd_match_abi_note(const Elf_Note *abi_note) -{ - const char *abi_name = (const char *) - ((const uint8_t *)abi_note + sizeof(*abi_note)); - - if (abi_note->n_namesz != sizeof("FreeBSD")) - return(FALSE); - if (memcmp(abi_name, "FreeBSD", sizeof("FreeBSD"))) - return(FALSE); - return(TRUE); -} +static const char DRAGONFLY_ABI_VENDOR[] = "DragonFly"; +static const char FREEBSD_ABI_VENDOR[] = "FreeBSD"; -static int -dragonfly_match_abi_note(const Elf_Note *abi_note) -{ - const char *abi_name = (const char *) - ((const uint8_t *)abi_note + sizeof(*abi_note)); - - if (abi_note->n_namesz != sizeof("DragonFly")) - return(FALSE); - if (memcmp(abi_name, "DragonFly", sizeof("DragonFly"))) - return(FALSE); - return(TRUE); -} +Elf_Brandnote __elfN(dragonfly_brandnote) = { + .hdr.n_namesz = sizeof(DRAGONFLY_ABI_VENDOR), + .hdr.n_descsz = sizeof(int32_t), + .hdr.n_type = 1, + .vendor = DRAGONFLY_ABI_VENDOR, + .flags = BN_TRANSLATE_OSREL, + .trans_osrel = __elfN(bsd_trans_osrel), +}; + +Elf_Brandnote __elfN(freebsd_brandnote) = { + .hdr.n_namesz = sizeof(FREEBSD_ABI_VENDOR), + .hdr.n_descsz = sizeof(int32_t), + .hdr.n_type = 1, + .vendor = FREEBSD_ABI_VENDOR, + .flags = BN_TRANSLATE_OSREL, + .trans_osrel = __elfN(bsd_trans_osrel), +}; int -elf_insert_brand_entry(Elf_Brandinfo *entry) +__elfN(insert_brand_entry)(Elf_Brandinfo *entry) { int i; - for (i=1; ip_sysent == info->entry->sysvec) { info->rval = TRUE; - return(-1); + return (-1); } - return(0); + return (0); } static int -elf_check_header(const Elf_Ehdr *hdr) +__elfN(check_header)(const Elf_Ehdr *hdr) { + Elf_Brandinfo *bi; + int i; + if (!IS_ELF(*hdr) || hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || hdr->e_ident[EI_DATA] != ELF_TARG_DATA || @@ -247,97 +218,25 @@ elf_check_header(const Elf_Ehdr *hdr) hdr->e_phentsize != sizeof(Elf_Phdr) || hdr->e_ehsize != sizeof(Elf_Ehdr) || hdr->e_version != ELF_TARG_VER) - return ENOEXEC; - - if (!ELF_MACHINE_OK(hdr->e_machine)) - return ENOEXEC; - - return 0; -} - -static Elf_Brandinfo * -elf_check_abi_note(struct image_params *imgp, const Elf_Phdr *ph) -{ - Elf_Brandinfo *match = NULL; - const Elf_Note *tmp_note; - struct lwbuf *lwb; - const char *page; - char *data = NULL; - Elf_Off off; - size_t firstoff; - size_t len; - size_t firstlen; - - len = ph->p_filesz; - off = ph->p_offset; - - firstoff = off & PAGE_MASK; - firstlen = PAGE_SIZE - firstoff; - - if (len < sizeof(Elf_Note) || len > PAGE_SIZE) - return NULL; /* ENOEXEC? */ - - if (exec_map_page(imgp, off >> PAGE_SHIFT, &lwb, &page)) - return NULL; + return (ENOEXEC); /* - * Crosses page boundary? Is that allowed? + * Make sure we have at least one brand for this machine. */ - if (firstlen < len) { - data = kmalloc(len, M_TEMP, M_WAITOK); - - bcopy(page + firstoff, data, firstlen); - exec_unmap_page(lwb); - if (exec_map_page(imgp, (off >> PAGE_SHIFT) + 1, &lwb, &page)) { - kfree(data, M_TEMP); - return NULL; - } - bcopy(page, data + firstlen, len - firstlen); - tmp_note = (void *)data; - } else { - tmp_note = (const void *)(page + firstoff); - } - - while (len >= sizeof(Elf_Note)) { - int i; - size_t nlen = roundup(tmp_note->n_namesz, sizeof(Elf_Word)) + - roundup(tmp_note->n_descsz, sizeof(Elf_Word)) + - sizeof(Elf_Note); - - if (nlen > len) - break; - - if (tmp_note->n_type != 1) - goto next; - - for (i = 0; i < MAX_BRANDS; i++) { - Elf_Brandinfo *bi = elf_brand_list[i]; - - if (bi != NULL && bi->match_abi_note != NULL && - bi->match_abi_note(tmp_note)) { - match = bi; - break; - } - } - - if (match != NULL) + for (i = 0; i < MAX_BRANDS; i++) { + bi = elf_brand_list[i]; + if (bi != NULL && bi->machine == hdr->e_machine) break; - -next: - len -= nlen; - tmp_note += nlen; } + if (i == MAX_BRANDS) + return (ENOEXEC); - if (data != NULL) - kfree(data, M_TEMP); - exec_unmap_page(lwb); - - return (match); + return (0); } static int -elf_load_section(struct proc *p, struct vmspace *vmspace, struct vnode *vp, +__elfN(load_section)(struct proc *p, struct vmspace *vmspace, struct vnode *vp, vm_offset_t offset, caddr_t vmaddr, size_t memsz, size_t filsz, vm_prot_t prot) { @@ -352,6 +251,8 @@ elf_load_section(struct proc *p, struct vmspace *vmspace, struct vnode *vp, object = vp->v_object; error = 0; + vm_object_hold(object); + /* * It's necessary to fail if the filsz + offset taken from the * header is greater than the actual file pager object's size. @@ -363,6 +264,7 @@ elf_load_section(struct proc *p, struct vmspace *vmspace, struct vnode *vp, */ if ((off_t)filsz + offset > vp->v_filesize || filsz > memsz) { uprintf("elf_load_section: truncated ELF file\n"); + vm_object_drop(object); return (ENOEXEC); } @@ -381,7 +283,7 @@ elf_load_section(struct proc *p, struct vmspace *vmspace, struct vnode *vp, map_len = round_page(offset+filsz) - file_addr; if (map_len != 0) { - vm_object_reference(object); + vm_object_reference_locked(object); /* cow flags: don't dump readonly sections in core */ cow = MAP_COPY_ON_WRITE | MAP_PREFAULT | @@ -401,12 +303,14 @@ elf_load_section(struct proc *p, struct vmspace *vmspace, struct vnode *vp, vm_map_entry_release(count); if (rv != KERN_SUCCESS) { vm_object_deallocate(object); - return EINVAL; + vm_object_drop(object); + return (EINVAL); } /* we can stop now if we've covered it all */ if (memsz == filsz) { - return 0; + vm_object_drop(object); + return (0); } } @@ -434,35 +338,39 @@ elf_load_section(struct proc *p, struct vmspace *vmspace, struct vnode *vp, vm_map_unlock(&vmspace->vm_map); vm_map_entry_release(count); if (rv != KERN_SUCCESS) { - return EINVAL; + vm_object_drop(object); + return (EINVAL); } } if (copy_len != 0) { vm_page_t m; struct lwbuf *lwb; + struct lwbuf lwb_cache; m = vm_fault_object_page(object, trunc_page(offset + filsz), VM_PROT_READ, 0, &error); if (m) { - lwb = lwbuf_alloc(m); + lwb = lwbuf_alloc(m, &lwb_cache); error = copyout((caddr_t)lwbuf_kva(lwb), (caddr_t)map_addr, copy_len); lwbuf_free(lwb); vm_page_unhold(m); } if (error) { + vm_object_drop(object); return (error); } } + vm_object_drop(object); /* * set it to the specified protection */ - vm_map_protect(&vmspace->vm_map, map_addr, map_addr + map_len, prot, - FALSE); + vm_map_protect(&vmspace->vm_map, map_addr, map_addr + map_len, + prot, FALSE); - return error; + return (error); } /* @@ -478,7 +386,7 @@ elf_load_section(struct proc *p, struct vmspace *vmspace, struct vnode *vp, * the entry point for the loaded file. */ static int -elf_load_file(struct proc *p, const char *file, u_long *addr, u_long *entry) +__elfN(load_file)(struct proc *p, const char *file, u_long *addr, u_long *entry) { struct { struct nlookupdata nd; @@ -491,6 +399,7 @@ elf_load_file(struct proc *p, const char *file, u_long *addr, u_long *entry) struct vmspace *vmspace = p->p_vmspace; struct vattr *attr; struct image_params *imgp; + struct mount *topmnt; vm_prot_t prot; u_long rbase; u_long base_addr = 0; @@ -515,6 +424,7 @@ elf_load_file(struct proc *p, const char *file, u_long *addr, u_long *entry) error = nlookup(nd); if (error == 0) error = cache_vget(&nd->nl_nch, nd->nl_cred, LK_EXCLUSIVE, &imgp->vp); + topmnt = nd->nl_nch.mount; nlookup_done(nd); if (error) goto fail; @@ -522,7 +432,7 @@ elf_load_file(struct proc *p, const char *file, u_long *addr, u_long *entry) /* * Check permissions, modes, uid, etc on the file, and "open" it. */ - error = exec_check_permissions(imgp); + error = exec_check_permissions(imgp, topmnt); if (error) { vn_unlock(imgp->vp); goto fail; @@ -540,7 +450,7 @@ elf_load_file(struct proc *p, const char *file, u_long *addr, u_long *entry) goto fail; hdr = (const Elf_Ehdr *)imgp->image_header; - if ((error = elf_check_header(hdr)) != 0) + if ((error = __elfN(check_header)(hdr)) != 0) goto fail; if (hdr->e_type == ET_DYN) rbase = *addr; @@ -551,8 +461,8 @@ elf_load_file(struct proc *p, const char *file, u_long *addr, u_long *entry) goto fail; } - /* Only support headers that fit within first page for now - * (multiplication of two Elf_Half fields will not overflow) */ + /* Only support headers that fit within first page for now */ + /* (multiplication of two Elf_Half fields will not overflow) */ if ((hdr->e_phoff > PAGE_SIZE) || (hdr->e_phentsize * hdr->e_phnum) > PAGE_SIZE - hdr->e_phoff) { error = ENOEXEC; @@ -560,9 +470,14 @@ elf_load_file(struct proc *p, const char *file, u_long *addr, u_long *entry) } phdr = (const Elf_Phdr *)(imgp->image_header + hdr->e_phoff); + if (!aligned(phdr, Elf_Addr)) { + error = ENOEXEC; + goto fail; + } for (i = 0, numsegs = 0; i < hdr->e_phnum; i++) { - if (phdr[i].p_type == PT_LOAD) { /* Loadable segment */ + if (phdr[i].p_type == PT_LOAD && phdr[i].p_memsz != 0) { + /* Loadable segment */ prot = 0; if (phdr[i].p_flags & PF_X) prot |= VM_PROT_EXECUTE; @@ -571,7 +486,7 @@ elf_load_file(struct proc *p, const char *file, u_long *addr, u_long *entry) if (phdr[i].p_flags & PF_R) prot |= VM_PROT_READ; - error = elf_load_section( + error = __elfN(load_section)( p, vmspace, imgp->vp, phdr[i].p_offset, (caddr_t)phdr[i].p_vaddr + @@ -590,7 +505,7 @@ elf_load_file(struct proc *p, const char *file, u_long *addr, u_long *entry) } } *addr = base_addr; - *entry=(unsigned long)hdr->e_entry + rbase; + *entry = (unsigned long)hdr->e_entry + rbase; fail: if (imgp->firstpage) @@ -601,49 +516,106 @@ fail: } kfree(tempdata, M_TEMP); - return error; + return (error); } -/* - * non static, as it can be overridden by start_init() - */ -int fallback_elf_brand = -1; -SYSCTL_INT(_kern, OID_AUTO, fallback_elf_brand, CTLFLAG_RW, - &fallback_elf_brand, -1, - "ELF brand of last resort"); +static Elf_Brandinfo * +__elfN(get_brandinfo)(struct image_params *imgp, const char *interp, + int32_t *osrel) +{ + const Elf_Ehdr *hdr = (const Elf_Ehdr *)imgp->image_header; + Elf_Brandinfo *bi; + boolean_t ret; + int i; -static int can_exec_dyn = 1; -SYSCTL_INT(_kern, OID_AUTO, elf_exec_dyn, CTLFLAG_RW, - &can_exec_dyn, 1, - "ELF: can exec shared libraries"); + /* We support four types of branding -- (1) the ELF EI_OSABI field + * that SCO added to the ELF spec, (2) FreeBSD 3.x's traditional string + * branding within the ELF header, (3) path of the `interp_path' field, + * and (4) the ".note.ABI-tag" ELF section. + */ + + /* Look for an ".note.ABI-tag" ELF section */ + for (i = 0; i < MAX_BRANDS; i++) { + bi = elf_brand_list[i]; + + if (bi == NULL) + continue; + if (hdr->e_machine == bi->machine && (bi->flags & + (BI_BRAND_NOTE|BI_BRAND_NOTE_MANDATORY)) != 0) { + ret = __elfN(check_note)(imgp, bi->brand_note, osrel); + if (ret) + return (bi); + } + } + + /* If the executable has a brand, search for it in the brand list. */ + for (i = 0; i < MAX_BRANDS; i++) { + bi = elf_brand_list[i]; + + if (bi == NULL || bi->flags & BI_BRAND_NOTE_MANDATORY) + continue; + if (hdr->e_machine == bi->machine && + (hdr->e_ident[EI_OSABI] == bi->brand || + strncmp((const char *)&hdr->e_ident[OLD_EI_BRAND], + bi->compat_3_brand, strlen(bi->compat_3_brand)) == 0)) + return (bi); + } + + /* Lacking a known brand, search for a recognized interpreter. */ + if (interp != NULL) { + for (i = 0; i < MAX_BRANDS; i++) { + bi = elf_brand_list[i]; + + if (bi == NULL || bi->flags & BI_BRAND_NOTE_MANDATORY) + continue; + if (hdr->e_machine == bi->machine && + strcmp(interp, bi->interp_path) == 0) + return (bi); + } + } + + /* Lacking a recognized interpreter, try the default brand */ + for (i = 0; i < MAX_BRANDS; i++) { + bi = elf_brand_list[i]; + + if (bi == NULL || bi->flags & BI_BRAND_NOTE_MANDATORY) + continue; + if (hdr->e_machine == bi->machine && + __elfN(fallback_brand) == bi->brand) + return (bi); + } + return (NULL); +} static int -exec_elf_imgact(struct image_params *imgp) +__CONCAT(exec_,__elfN(imgact))(struct image_params *imgp) { const Elf_Ehdr *hdr = (const Elf_Ehdr *) imgp->image_header; const Elf_Phdr *phdr; - Elf_Auxargs *elf_auxargs = NULL; + Elf_Auxargs *elf_auxargs; struct vmspace *vmspace; vm_prot_t prot; u_long text_size = 0, data_size = 0, total_size = 0; u_long text_addr = 0, data_addr = 0; u_long seg_size, seg_addr; - u_long addr, entry = 0, proghdr = 0; - int error, i; - const char *interp = NULL; - const Elf_Note *abi_note = NULL; - Elf_Brandinfo *brand_info = NULL; + u_long addr, baddr, et_dyn_addr, entry = 0, proghdr = 0; + int32_t osrel = 0; + int error = 0, i, n; + boolean_t failure; + char *interp = NULL; + const char *newinterp = NULL; + Elf_Brandinfo *brand_info; char *path; - error = 0; - /* * Do we have a valid ELF header ? - * We allow execution of ET_EXEC and, if kern.elf_exec_dyn is 1, ET_DYN. + * + * Only allow ET_EXEC & ET_DYN here, reject ET_DYN later if a particular + * brand doesn't support it. Both DragonFly platforms do by default. */ - if (elf_check_header(hdr) != 0 || - (hdr->e_type != ET_EXEC && (!can_exec_dyn || hdr->e_type != ET_DYN))) - return -1; + if (__elfN(check_header)(hdr) != 0 || + (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN)) + return (-1); /* * From here on down, we return an errno, not -1, as we've @@ -653,13 +625,71 @@ exec_elf_imgact(struct image_params *imgp) if ((hdr->e_phoff > PAGE_SIZE) || (hdr->e_phoff + hdr->e_phentsize * hdr->e_phnum) > PAGE_SIZE) { /* Only support headers in first page for now */ - return ENOEXEC; + return (ENOEXEC); + } + phdr = (const Elf_Phdr *)(imgp->image_header + hdr->e_phoff); + if (!aligned(phdr, Elf_Addr)) + return (ENOEXEC); + n = 0; + baddr = 0; + for (i = 0; i < hdr->e_phnum; i++) { + if (phdr[i].p_type == PT_LOAD) { + if (n == 0) + baddr = phdr[i].p_vaddr; + n++; + continue; + } + if (phdr[i].p_type == PT_INTERP) { + /* + * If interp is already defined there are more than + * one PT_INTERP program headers present. Take only + * the first one and ignore the rest. + */ + if (interp != NULL) + continue; + + if (phdr[i].p_filesz == 0 || + phdr[i].p_filesz > PAGE_SIZE || + phdr[i].p_filesz > MAXPATHLEN) + return (ENOEXEC); + + interp = kmalloc(phdr[i].p_filesz, M_TEMP, M_WAITOK); + failure = extract_interpreter(imgp, &phdr[i], interp); + if (failure) { + kfree(interp, M_TEMP); + return (ENOEXEC); + } + continue; + } } - phdr = (const Elf_Phdr*)(imgp->image_header + hdr->e_phoff); - /* - * From this point on, we may have resources that need to be freed. - */ + brand_info = __elfN(get_brandinfo)(imgp, interp, &osrel); + if (brand_info == NULL) { + uprintf("ELF binary type \"%u\" not known.\n", + hdr->e_ident[EI_OSABI]); + if (interp != NULL) + kfree(interp, M_TEMP); + return (ENOEXEC); + } + if (hdr->e_type == ET_DYN) { + if ((brand_info->flags & BI_CAN_EXEC_DYN) == 0) { + if (interp != NULL) + kfree(interp, M_TEMP); + return (ENOEXEC); + } + /* + * Honour the base load address from the dso if it is + * non-zero for some reason. + */ + if (baddr == 0) + et_dyn_addr = ET_DYN_LOAD_ADDR; + else + et_dyn_addr = 0; + } else + et_dyn_addr = 0; + + if (interp != NULL && brand_info->interp_newpath != NULL) + newinterp = brand_info->interp_newpath; exec_new_vmspace(imgp, NULL); @@ -674,9 +704,11 @@ exec_elf_imgact(struct image_params *imgp) vmspace = imgp->proc->p_vmspace; for (i = 0; i < hdr->e_phnum; i++) { - switch(phdr[i].p_type) { + switch (phdr[i].p_type) { case PT_LOAD: /* Loadable segment */ + if (phdr[i].p_memsz == 0) + break; prot = 0; if (phdr[i].p_flags & PF_X) prot |= VM_PROT_EXECUTE; @@ -685,13 +717,19 @@ exec_elf_imgact(struct image_params *imgp) if (phdr[i].p_flags & PF_R) prot |= VM_PROT_READ; - if ((error = elf_load_section(imgp->proc, - vmspace, imgp->vp, - phdr[i].p_offset, - (caddr_t)phdr[i].p_vaddr, - phdr[i].p_memsz, - phdr[i].p_filesz, prot)) != 0) - goto fail; + if ((error = __elfN(load_section)( + imgp->proc, + vmspace, + imgp->vp, + phdr[i].p_offset, + (caddr_t)phdr[i].p_vaddr + et_dyn_addr, + phdr[i].p_memsz, + phdr[i].p_filesz, + prot)) != 0) { + if (interp != NULL) + kfree (interp, M_TEMP); + return (error); + } /* * If this segment contains the program headers, @@ -702,11 +740,12 @@ exec_elf_imgact(struct image_params *imgp) if (phdr[i].p_offset == 0 && hdr->e_phoff + hdr->e_phnum * hdr->e_phentsize <= phdr[i].p_filesz) - proghdr = phdr[i].p_vaddr + hdr->e_phoff; + proghdr = phdr[i].p_vaddr + hdr->e_phoff + + et_dyn_addr; - seg_addr = trunc_page(phdr[i].p_vaddr); + seg_addr = trunc_page(phdr[i].p_vaddr + et_dyn_addr); seg_size = round_page(phdr[i].p_memsz + - phdr[i].p_vaddr - seg_addr); + phdr[i].p_vaddr + et_dyn_addr - seg_addr); /* * Is this .text or .data? We can't use @@ -728,7 +767,7 @@ exec_elf_imgact(struct image_params *imgp) phdr[i].p_memsz)) { text_size = seg_size; text_addr = seg_addr; - entry = (u_long)hdr->e_entry; + entry = (u_long)hdr->e_entry + et_dyn_addr; } else { data_size = seg_size; data_addr = seg_addr; @@ -745,24 +784,14 @@ exec_elf_imgact(struct image_params *imgp) text_size > maxtsiz || total_size > imgp->proc->p_rlimit[RLIMIT_VMEM].rlim_cur) { + if (interp != NULL) + kfree(interp, M_TEMP); error = ENOMEM; - goto fail; - } - break; - case PT_INTERP: /* Path to interpreter */ - if (phdr[i].p_filesz > MAXPATHLEN || - phdr[i].p_offset + phdr[i].p_filesz > PAGE_SIZE) { - error = ENOEXEC; - goto fail; + return (error); } - interp = imgp->image_header + phdr[i].p_offset; - break; - case PT_NOTE: /* Check for .note.ABI-tag */ - if (brand_info == NULL) - brand_info = elf_check_abi_note(imgp, &phdr[i]); break; case PT_PHDR: /* Program header table info */ - proghdr = phdr[i].p_vaddr; + proghdr = phdr[i].p_vaddr + et_dyn_addr; break; default: break; @@ -778,104 +807,40 @@ exec_elf_imgact(struct image_params *imgp) imgp->entry_addr = entry; - /* We support three types of branding -- (1) the ELF EI_OSABI field - * that SCO added to the ELF spec, (2) FreeBSD 3.x's traditional string - * branding w/in the ELF header, and (3) path of the `interp_path' - * field. We should also look for an ".note.ABI-tag" ELF section now - * in all Linux ELF binaries, FreeBSD 4.1+, and some NetBSD ones. - */ + imgp->proc->p_sysent = brand_info->sysvec; + EVENTHANDLER_INVOKE(process_exec, imgp); - /* If the executable has a brand, search for it in the brand list. */ - if (brand_info == NULL && hdr->e_ident[EI_OSABI] != ELFOSABI_NONE) { - for (i = 0; i < MAX_BRANDS; i++) { - Elf_Brandinfo *bi = elf_brand_list[i]; - - if (bi != NULL && - (hdr->e_ident[EI_OSABI] == bi->brand - || 0 == - strncmp((const char *)&hdr->e_ident[OLD_EI_BRAND], - bi->compat_3_brand, strlen(bi->compat_3_brand)))) { - brand_info = bi; - break; - } + if (interp != NULL) { + int have_interp = FALSE; + if (brand_info->emul_path != NULL && + brand_info->emul_path[0] != '\0') { + path = kmalloc(MAXPATHLEN, M_TEMP, M_WAITOK); + ksnprintf(path, MAXPATHLEN, "%s%s", + brand_info->emul_path, interp); + error = __elfN(load_file)(imgp->proc, path, &addr, + &imgp->entry_addr); + kfree(path, M_TEMP); + if (error == 0) + have_interp = TRUE; } - } - - /* Search for a recognized ABI. */ - if (brand_info == NULL && abi_note != NULL) { - } - - /* - * ELFOSABI_NONE == ELFOSABI_SYSV, so a SYSV binary misses all - * checks so far, since it is neither branded nor does it have - * an ABI note. If the EI_OSABI field is ELFOSABI_NONE, assume - * it is svr4 and look for an entry in the elf_brand_list with - * match_abi_note == NULL. - */ - if (brand_info == NULL && hdr->e_ident[EI_OSABI] == ELFOSABI_NONE) { - for (i = 0; i < MAX_BRANDS; i++) { - Elf_Brandinfo *bi = elf_brand_list[i]; - - if (bi != NULL && bi->match_abi_note == NULL && - ELFOSABI_SYSV == bi->brand) { - brand_info = bi; - break; - } + if (!have_interp && newinterp != NULL) { + error = __elfN(load_file)(imgp->proc, newinterp, + &addr, &imgp->entry_addr); + if (error == 0) + have_interp = TRUE; } - } - - /* Lacking a recognized ABI, search for a recognized interpreter. */ - if (brand_info == NULL && interp != NULL) { - for (i = 0; i < MAX_BRANDS; i++) { - Elf_Brandinfo *bi = elf_brand_list[i]; - - if (bi != NULL && - strcmp(interp, bi->interp_path) == 0) { - brand_info = bi; - break; - } + if (!have_interp) { + error = __elfN(load_file)(imgp->proc, interp, &addr, + &imgp->entry_addr); } - } - - /* Lacking a recognized interpreter, try the default brand */ - if (brand_info == NULL) { - for (i = 0; i < MAX_BRANDS; i++) { - Elf_Brandinfo *bi = elf_brand_list[i]; - - if (bi != NULL && fallback_elf_brand == bi->brand) { - brand_info = bi; - break; - } + if (error != 0) { + uprintf("ELF interpreter %s not found\n", interp); + kfree(interp, M_TEMP); + return (error); } - } - - if (brand_info == NULL) { - uprintf("ELF binary type \"%u\" not known.\n", - hdr->e_ident[EI_OSABI]); - error = ENOEXEC; - goto fail; - } - - imgp->proc->p_sysent = brand_info->sysvec; - EVENTHANDLER_INVOKE(process_exec, imgp); - - if (interp != NULL) { - path = kmalloc(MAXPATHLEN, M_TEMP, M_WAITOK); - ksnprintf(path, MAXPATHLEN, "%s%s", - brand_info->emul_path, interp); - if ((error = elf_load_file(imgp->proc, path, &addr, - &imgp->entry_addr)) != 0) { - if ((error = elf_load_file(imgp->proc, interp, &addr, - &imgp->entry_addr)) != 0) { - uprintf("ELF interpreter %s not found\n", path); - kfree(path, M_TEMP); - goto fail; - } - } - kfree(path, M_TEMP); - } else { - addr = 0; - } + kfree(interp, M_TEMP); + } else + addr = et_dyn_addr; /* * Construct auxargs table (used by the fixup routine) @@ -889,29 +854,26 @@ exec_elf_imgact(struct image_params *imgp) elf_auxargs->base = addr; elf_auxargs->flags = 0; elf_auxargs->entry = entry; - elf_auxargs->trace = elf_trace; imgp->auxargs = elf_auxargs; imgp->interpreted = 0; + imgp->proc->p_osrel = osrel; -fail: - return error; + return (error); } -static int -elf_freebsd_fixup(register_t **stack_base, struct image_params *imgp) +int +__elfN(dragonfly_fixup)(register_t **stack_base, struct image_params *imgp) { Elf_Auxargs *args = (Elf_Auxargs *)imgp->auxargs; - register_t *pos; + Elf_Addr *base; + Elf_Addr *pos; - pos = *stack_base + (imgp->args->argc + imgp->args->envc + 2); + base = (Elf_Addr *)*stack_base; + pos = base + (imgp->args->argc + imgp->args->envc + 2); - if (args->trace) { - AUXARGS_ENTRY(pos, AT_DEBUG, 1); - } - if (args->execfd != -1) { + if (args->execfd != -1) AUXARGS_ENTRY(pos, AT_EXECFD, args->execfd); - } AUXARGS_ENTRY(pos, AT_PHDR, args->phdr); AUXARGS_ENTRY(pos, AT_PHENT, args->phent); AUXARGS_ENTRY(pos, AT_PHNUM, args->phnum); @@ -919,21 +881,25 @@ elf_freebsd_fixup(register_t **stack_base, struct image_params *imgp) AUXARGS_ENTRY(pos, AT_FLAGS, args->flags); AUXARGS_ENTRY(pos, AT_ENTRY, args->entry); AUXARGS_ENTRY(pos, AT_BASE, args->base); + if (imgp->execpathp != 0) + AUXARGS_ENTRY(pos, AT_EXECPATH, imgp->execpathp); + AUXARGS_ENTRY(pos, AT_OSRELDATE, osreldate); AUXARGS_ENTRY(pos, AT_NULL, 0); kfree(imgp->auxargs, M_TEMP); imgp->auxargs = NULL; - (*stack_base)--; - suword(*stack_base, (long) imgp->args->argc); - return 0; -} + base--; + suword(base, (long)imgp->args->argc); + *stack_base = (register_t *)base; + return (0); +} /* * Code for generating ELF core dumps. */ -typedef int (*segment_callback) (vm_map_entry_t, void *); +typedef int (*segment_callback)(vm_map_entry_t, void *); /* Closure for cb_put_phdr(). */ struct phdr_closure { @@ -971,22 +937,20 @@ static int cb_put_fp(vm_map_entry_t, void *); static int each_segment (struct proc *, segment_callback, void *, int); -static int elf_corehdr (struct lwp *, int, struct file *, struct ucred *, +static int __elfN(corehdr)(struct lwp *, int, struct file *, struct ucred *, int, elf_buf_t); enum putmode { WRITE, DRYRUN }; -static int elf_puthdr (struct lwp *, elf_buf_t, int sig, enum putmode, +static int __elfN(puthdr)(struct lwp *, elf_buf_t, int sig, enum putmode, int, struct file *); static int elf_putallnotes(struct lwp *, elf_buf_t, int, enum putmode); -static int elf_putnote (elf_buf_t, const char *, int, const void *, size_t); +static int __elfN(putnote)(elf_buf_t, const char *, int, const void *, size_t); static int elf_putsigs(struct lwp *, elf_buf_t); static int elf_puttextvp(struct proc *, elf_buf_t); static int elf_putfiles(struct proc *, elf_buf_t, struct file *); -extern int osreldate; - int -elf_coredump(struct lwp *lp, int sig, struct vnode *vp, off_t limit) +__elfN(coredump)(struct lwp *lp, int sig, struct vnode *vp, off_t limit) { struct file *fp; int error; @@ -1002,7 +966,6 @@ elf_coredump(struct lwp *lp, int sig, struct vnode *vp, off_t limit) fp->f_flag = O_CREAT|O_WRONLY|O_NOFOLLOW; fp->f_ops = &vnode_fileops; fp->f_data = vp; - vn_unlock(vp); error = generic_elf_coredump(lp, sig, fp, limit); @@ -1039,7 +1002,7 @@ generic_elf_coredump(struct lwp *lp, int sig, struct file *fp, off_t limit) * size is calculated. */ bzero(&target, sizeof(target)); - elf_puthdr(lp, &target, sig, DRYRUN, seginfo.count, fp); + __elfN(puthdr)(lp, &target, sig, DRYRUN, seginfo.count, fp); if (target.off + seginfo.vsize >= limit) return (EFAULT); @@ -1052,7 +1015,7 @@ generic_elf_coredump(struct lwp *lp, int sig, struct file *fp, off_t limit) target.off = 0; target.buf = kmalloc(target.off_max, M_TEMP, M_WAITOK|M_ZERO); - error = elf_corehdr(lp, sig, fp, cred, seginfo.count, &target); + error = __elfN(corehdr)(lp, sig, fp, cred, seginfo.count, &target); /* Write the contents of all of the writable segments. */ if (error == 0) { @@ -1071,7 +1034,7 @@ generic_elf_coredump(struct lwp *lp, int sig, struct file *fp, off_t limit) } kfree(target.buf, M_TEMP); - return error; + return (error); } /* @@ -1085,7 +1048,7 @@ cb_put_phdr(vm_map_entry_t entry, void *closure) Elf_Phdr *phdr = phc->phdr; if (phc->phdr == phc->phdr_max) - return EINVAL; + return (EINVAL); phc->offset = round_page(phc->offset); @@ -1105,7 +1068,7 @@ cb_put_phdr(vm_map_entry_t entry, void *closure) phc->offset += phdr->p_filesz; ++phc->phdr; - return 0; + return (0); } /* @@ -1119,7 +1082,7 @@ cb_size_segment(vm_map_entry_t entry, void *closure) ++ssc->count; ssc->vsize += entry->end - entry->start; - return 0; + return (0); } /* @@ -1135,10 +1098,10 @@ cb_fpcount_segment(vm_map_entry_t entry, void *closure) if (entry->object.vm_object->type == OBJT_VNODE) { vp = (struct vnode *)entry->object.vm_object->handle; if ((vp->v_flag & VCKPT) && curproc->p_textvp == vp) - return 0; + return (0); ++*count; } - return 0; + return (0); } static int @@ -1171,9 +1134,9 @@ cb_put_fp(vm_map_entry_t entry, void *closure) if (entry->object.vm_object->type == OBJT_VNODE) { vp = (struct vnode *)entry->object.vm_object->handle; if ((vp->v_flag & VCKPT) && curproc->p_textvp == vp) - return 0; + return (0); if (vnh == fpc->vnh_max) - return EINVAL; + return (EINVAL); if (vp->v_mount) vnh->vnh_fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid; @@ -1206,7 +1169,7 @@ cb_put_fp(vm_map_entry_t entry, void *closure) ++fpc->vnh; ++fpc->count; } - return 0; + return (0); } /* @@ -1224,6 +1187,8 @@ each_segment(struct proc *p, segment_callback func, void *closure, int writable) for (entry = map->header.next; error == 0 && entry != &map->header; entry = entry->next) { vm_object_t obj; + vm_object_t lobj; + vm_object_t tobj; /* * Don't dump inaccessible mappings, deal with legacy @@ -1256,19 +1221,42 @@ each_segment(struct proc *p, segment_callback func, void *closure, int writable) if ((obj = entry->object.vm_object) == NULL) continue; - /* Find the deepest backing object. */ - while (obj->backing_object != NULL) - obj = obj->backing_object; - - /* Ignore memory-mapped devices and such things. */ - if (obj->type != OBJT_DEFAULT && - obj->type != OBJT_SWAP && - obj->type != OBJT_VNODE) - continue; + /* + * Find the bottom-most object, leaving the base object + * and the bottom-most object held (but only one hold + * if they happen to be the same). + */ + vm_object_hold(obj); + + lobj = obj; + while (lobj && (tobj = lobj->backing_object) != NULL) { + KKASSERT(tobj != obj); + vm_object_hold(tobj); + if (tobj == lobj->backing_object) { + if (lobj != obj) { + vm_object_lock_swap(); + vm_object_drop(lobj); + } + lobj = tobj; + } else { + vm_object_drop(tobj); + } + } - error = (*func)(entry, closure); + /* + * The callback only applies to default, swap, or vnode + * objects. Other types of objects such as memory-mapped + * devices are ignored. + */ + if (lobj->type == OBJT_DEFAULT || lobj->type == OBJT_SWAP || + lobj->type == OBJT_VNODE) { + error = (*func)(entry, closure); + } + if (lobj != obj) + vm_object_drop(lobj); + vm_object_drop(obj); } - return error; + return (error); } static @@ -1292,7 +1280,7 @@ target_reserve(elf_buf_t target, size_t bytes, int *error) * the page boundary. */ static int -elf_corehdr(struct lwp *lp, int sig, struct file *fp, struct ucred *cred, +__elfN(corehdr)(struct lwp *lp, int sig, struct file *fp, struct ucred *cred, int numsegs, elf_buf_t target) { int error; @@ -1303,18 +1291,18 @@ elf_corehdr(struct lwp *lp, int sig, struct file *fp, struct ucred *cred, * a checkpoint file pointer within the core file itself, because * it may not be restored from the same file handle. */ - error = elf_puthdr(lp, target, sig, WRITE, numsegs, fp); + error = __elfN(puthdr)(lp, target, sig, WRITE, numsegs, fp); /* Write it to the core file. */ if (error == 0) { error = fp_write(fp, target->buf, target->off, &nbytes, UIO_SYSSPACE); } - return error; + return (error); } static int -elf_puthdr(struct lwp *lp, elf_buf_t target, int sig, enum putmode mode, +__elfN(puthdr)(struct lwp *lp, elf_buf_t target, int sig, enum putmode mode, int numsegs, struct file *fp) { struct proc *p = lp->lwp_proc; @@ -1449,16 +1437,17 @@ elf_putallnotes(struct lwp *corelp, elf_buf_t target, int sig, if (mode != DRYRUN) { psinfo->pr_version = PRPSINFO_VERSION; psinfo->pr_psinfosz = sizeof(prpsinfo_t); - strncpy(psinfo->pr_fname, p->p_comm, - sizeof(psinfo->pr_fname) - 1); + strlcpy(psinfo->pr_fname, p->p_comm, + sizeof(psinfo->pr_fname)); /* * XXX - We don't fill in the command line arguments * properly yet. */ - strncpy(psinfo->pr_psargs, p->p_comm, PRARGSZ); + strlcpy(psinfo->pr_psargs, p->p_comm, + sizeof(psinfo->pr_psargs)); } error = - elf_putnote(target, "CORE", NT_PRPSINFO, psinfo, sizeof *psinfo); + __elfN(putnote)(target, "CORE", NT_PRPSINFO, psinfo, sizeof *psinfo); if (error) goto exit; @@ -1483,11 +1472,11 @@ elf_putallnotes(struct lwp *corelp, elf_buf_t target, int sig, fill_fpregs(corelp, fpregs); } error = - elf_putnote(target, "CORE", NT_PRSTATUS, status, sizeof *status); + __elfN(putnote)(target, "CORE", NT_PRSTATUS, status, sizeof *status); if (error) goto exit; error = - elf_putnote(target, "CORE", NT_FPREGSET, fpregs, sizeof *fpregs); + __elfN(putnote)(target, "CORE", NT_FPREGSET, fpregs, sizeof *fpregs); if (error) goto exit; @@ -1505,11 +1494,11 @@ elf_putallnotes(struct lwp *corelp, elf_buf_t target, int sig, fill_regs(lp, &status->pr_reg); fill_fpregs(lp, fpregs); } - error = elf_putnote(target, "CORE", NT_PRSTATUS, + error = __elfN(putnote)(target, "CORE", NT_PRSTATUS, status, sizeof *status); if (error) goto exit; - error = elf_putnote(target, "CORE", NT_FPREGSET, + error = __elfN(putnote)(target, "CORE", NT_FPREGSET, fpregs, sizeof *fpregs); if (error) goto exit; @@ -1527,7 +1516,7 @@ exit: * NOTE: 4-byte alignment. */ static int -elf_putnote(elf_buf_t target, const char *name, int type, +__elfN(putnote)(elf_buf_t target, const char *name, int type, const void *desc, size_t descsz) { int error = 0; @@ -1548,7 +1537,7 @@ elf_putnote(elf_buf_t target, const char *name, int type, if (dst != NULL) bcopy(desc, dst, note.n_descsz); target->off = roundup2(target->off, sizeof(Elf_Word)); - return(error); + return (error); } @@ -1569,7 +1558,7 @@ elf_putsigs(struct lwp *lp, elf_buf_t target) sizeof(sigset_t)); csi->csi_sigparent = p->p_sigparent; } - return(error); + return (error); } static int @@ -1642,7 +1631,7 @@ elf_putfiles(struct proc *p, elf_buf_t target, struct file *ckfp) } fdrop(fp); } - return(error); + return (error); } static int @@ -1674,12 +1663,194 @@ elf_puttextvp(struct proc *p, elf_buf_t target) error = each_segment(p, cb_fpcount_segment, &fpc.count, 0); } target->off += fpc.count * sizeof(struct vn_hdr); - return(error); + return (error); +} + +/* + * Try to find the appropriate ABI-note section for checknote, + * The entire image is searched if necessary, not only the first page. + */ +static boolean_t +__elfN(check_note)(struct image_params *imgp, Elf_Brandnote *checknote, + int32_t *osrel) +{ + boolean_t valid_note_found; + const Elf_Phdr *phdr, *pnote; + const Elf_Ehdr *hdr; + int i; + + valid_note_found = FALSE; + hdr = (const Elf_Ehdr *)imgp->image_header; + phdr = (const Elf_Phdr *)(imgp->image_header + hdr->e_phoff); + + for (i = 0; i < hdr->e_phnum; i++) { + if (phdr[i].p_type == PT_NOTE) { + pnote = &phdr[i]; + valid_note_found = check_PT_NOTE (imgp, checknote, + osrel, pnote); + if (valid_note_found) + break; + } + } + return valid_note_found; +} + +static boolean_t +check_PT_NOTE(struct image_params *imgp, Elf_Brandnote *checknote, + int32_t *osrel, const Elf_Phdr * pnote) +{ + boolean_t limited_to_first_page; + boolean_t found = FALSE; + const Elf_Note *note, *note0, *note_end; + const char *note_name; + __ElfN(Off) noteloc, firstloc; + __ElfN(Size) notesz, firstlen, endbyte; + struct lwbuf *lwb; + struct lwbuf lwb_cache; + const char *page; + char *data = NULL; + int n; + + notesz = pnote->p_filesz; + noteloc = pnote->p_offset; + endbyte = noteloc + notesz; + limited_to_first_page = noteloc < PAGE_SIZE && endbyte < PAGE_SIZE; + + if (limited_to_first_page) { + note = (const Elf_Note *)(imgp->image_header + noteloc); + note_end = (const Elf_Note *)(imgp->image_header + endbyte); + note0 = note; + } else { + firstloc = noteloc & PAGE_MASK; + firstlen = PAGE_SIZE - firstloc; + if (notesz < sizeof(Elf_Note) || notesz > PAGE_SIZE) + return (FALSE); + + lwb = &lwb_cache; + if (exec_map_page(imgp, noteloc >> PAGE_SHIFT, &lwb, &page)) + return (FALSE); + if (firstlen < notesz) { /* crosses page boundary */ + data = kmalloc(notesz, M_TEMP, M_WAITOK); + bcopy(page + firstloc, data, firstlen); + + exec_unmap_page(lwb); + lwb = &lwb_cache; + if (exec_map_page(imgp, (noteloc >> PAGE_SHIFT) + 1, + &lwb, &page)) { + kfree(data, M_TEMP); + return (FALSE); + } + bcopy(page, data + firstlen, notesz - firstlen); + note = note0 = (const Elf_Note *)(data); + note_end = (const Elf_Note *)(data + notesz); + } else { + note = note0 = (const Elf_Note *)(page + firstloc); + note_end = (const Elf_Note *)(page + firstloc + + firstlen); + } + } + + for (n = 0; n < 100 && note >= note0 && note < note_end; n++) { + if (!aligned(note, Elf32_Addr)) + break; + note_name = (const char *)(note + 1); + + if (note->n_namesz == checknote->hdr.n_namesz + && note->n_descsz == checknote->hdr.n_descsz + && note->n_type == checknote->hdr.n_type + && (strncmp(checknote->vendor, note_name, + checknote->hdr.n_namesz) == 0)) { + /* Fetch osreldata from ABI.note-tag */ + if ((checknote->flags & BN_TRANSLATE_OSREL) != 0 && + checknote->trans_osrel != NULL) + checknote->trans_osrel(note, osrel); + found = TRUE; + break; + } + note = (const Elf_Note *)((const char *)(note + 1) + + roundup2(note->n_namesz, sizeof(Elf32_Addr)) + + roundup2(note->n_descsz, sizeof(Elf32_Addr))); + } + + if (!limited_to_first_page) { + if (data != NULL) + kfree(data, M_TEMP); + exec_unmap_page(lwb); + } + return (found); +} + +/* + * The interpreter program header may be located beyond the first page, so + * regardless of its location, a copy of the interpreter path is created so + * that it may be safely referenced by the calling function in all case. The + * memory is allocated by calling function, and the copying is done here. + */ +static boolean_t +extract_interpreter(struct image_params *imgp, const Elf_Phdr *pinterpreter, + char *data) +{ + boolean_t limited_to_first_page; + const boolean_t result_success = FALSE; + const boolean_t result_failure = TRUE; + __ElfN(Off) pathloc, firstloc; + __ElfN(Size) pathsz, firstlen, endbyte; + struct lwbuf *lwb; + struct lwbuf lwb_cache; + const char *page; + + pathsz = pinterpreter->p_filesz; + pathloc = pinterpreter->p_offset; + endbyte = pathloc + pathsz; + + limited_to_first_page = pathloc < PAGE_SIZE && endbyte < PAGE_SIZE; + if (limited_to_first_page) { + bcopy(imgp->image_header + pathloc, data, pathsz); + return (result_success); + } + + firstloc = pathloc & PAGE_MASK; + firstlen = PAGE_SIZE - firstloc; + + lwb = &lwb_cache; + if (exec_map_page(imgp, pathloc >> PAGE_SHIFT, &lwb, &page)) + return (result_failure); + + if (firstlen < pathsz) { /* crosses page boundary */ + bcopy(page + firstloc, data, firstlen); + + exec_unmap_page(lwb); + lwb = &lwb_cache; + if (exec_map_page(imgp, (pathloc >> PAGE_SHIFT) + 1, &lwb, + &page)) + return (result_failure); + bcopy(page, data + firstlen, pathsz - firstlen); + } else + bcopy(page + firstloc, data, pathsz); + + exec_unmap_page(lwb); + return (result_success); } +static boolean_t +__elfN(bsd_trans_osrel)(const Elf_Note *note, int32_t *osrel) +{ + uintptr_t p; + + p = (uintptr_t)(note + 1); + p += roundup2(note->n_namesz, sizeof(Elf32_Addr)); + *osrel = *(const int32_t *)(p); + + return (TRUE); +} /* * Tell kern_execve.c about it, with a little help from the linker. */ -static struct execsw elf_execsw = {exec_elf_imgact, "ELF"}; -EXEC_SET_ORDERED(elf, elf_execsw, SI_ORDER_FIRST); +#if defined(__x86_64__) +static struct execsw elf_execsw = {exec_elf64_imgact, "ELF64"}; +EXEC_SET_ORDERED(elf64, elf_execsw, SI_ORDER_FIRST); +#else /* i386 assumed */ +static struct execsw elf_execsw = {exec_elf32_imgact, "ELF32"}; +EXEC_SET_ORDERED(elf32, elf_execsw, SI_ORDER_FIRST); +#endif