Some cleanups and optimizations:

- Use the system headers method for Elf32/Elf64 symbol compatability
- get rid of the UPRINTF debugging.
- check the ELF header for compatability much more completely
- optimize the section mapper.  Use the same direct VM interfaces that
  imgact_aout.c and kern_exec.c use.
- Check the return codes from the vm_* functions better.  Some return
  KERN_* results, not an errno.
- prefault the page tables to reduce startup faults on page tables like
  a.out does.
- reset the segment protection to zero for each loop, otherwise each
  segment could get progressively more privs. (eg: if the first was
  read/write/execute, and the second was meant to be read/execute, the
  bug would make the second r/w/x too.  In practice this was not a
  problem because executables are normally laid out with text first.)
- Don't impose arbitary limits.  Use the limits on headers imposed by
  the need to fit them into one page.
- Remove unused switch() cases now that the verbose debugging is gone.

I've been using an earlier version of this for a month or so.
This sped up ELF exec speed a bit for me but I found it hard to get
consistant benchmarks when I tested it last (a few weeks ago).
I'm still bothered by the page read out of order caused by the
transition from data to bss. This which requires either part filling the
transition page or clearing the remainder.
This commit is contained in:
Peter Wemm 1998-10-18 15:55:12 +00:00
parent 034db87204
commit 52c24af701
1 changed files with 111 additions and 166 deletions

View File

@ -26,7 +26,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* $Id: imgact_elf.c,v 1.39 1998/10/15 09:52:19 dfr Exp $ * $Id: imgact_elf.c,v 1.40 1998/10/16 03:55:00 peter Exp $
*/ */
#include "opt_rlimit.h" #include "opt_rlimit.h"
@ -63,40 +63,25 @@
#include <vm/vm_prot.h> #include <vm/vm_prot.h>
#include <vm/vm_extern.h> #include <vm/vm_extern.h>
#include <machine/elf.h>
#include <machine/md_var.h> #include <machine/md_var.h>
#define MAX_PHDR 32 /* XXX enough ? */ __ElfType(Brandinfo);
__ElfType(Auxargs);
#if ELF_TARG_CLASS == ELFCLASS32
#define Elf_Ehdr Elf32_Ehdr
#define Elf_Phdr Elf32_Phdr
#define Elf_Auxargs Elf32_Auxargs
#define Elf_Brandinfo Elf32_Brandinfo
#else
#define Elf_Ehdr Elf64_Ehdr
#define Elf_Phdr Elf64_Phdr
#define Elf_Auxargs Elf64_Auxargs
#define Elf_Brandinfo Elf64_Brandinfo
#endif
static int elf_check_header __P((const Elf_Ehdr *hdr, int type)); static int elf_check_header __P((const Elf_Ehdr *hdr, int type));
static int elf_freebsd_fixup __P((long **stack_base, static int elf_freebsd_fixup __P((long **stack_base,
struct image_params *imgp)); struct image_params *imgp));
static int elf_load_file __P((struct proc *p, char *file, u_long *addr, static int elf_load_file __P((struct proc *p, char *file, u_long *addr,
u_long *entry)); u_long *entry));
static int elf_load_section __P((struct vmspace *vmspace, struct vnode *vp, static int elf_load_section __P((struct proc *p,
struct vmspace *vmspace, struct vnode *vp,
vm_offset_t offset, caddr_t vmaddr, size_t memsz, size_t filsz, vm_offset_t offset, caddr_t vmaddr, size_t memsz, size_t filsz,
vm_prot_t prot)); vm_prot_t prot));
static int exec_elf_imgact __P((struct image_params *imgp)); static int exec_elf_imgact __P((struct image_params *imgp));
static int elf_trace = 0; static int elf_trace = 0;
SYSCTL_INT(_debug, OID_AUTO, elf_trace, CTLFLAG_RW, &elf_trace, 0, ""); SYSCTL_INT(_debug, OID_AUTO, elf_trace, CTLFLAG_RW, &elf_trace, 0, "");
#define UPRINTF if (elf_trace) uprintf
static struct sysentvec elf_freebsd_sysvec = { static struct sysentvec elf_freebsd_sysvec = {
SYS_MAXSYSCALL, SYS_MAXSYSCALL,
@ -163,86 +148,124 @@ elf_remove_brand_entry(Elf_Brandinfo *entry)
static int static int
elf_check_header(const Elf_Ehdr *hdr, int type) elf_check_header(const Elf_Ehdr *hdr, int type)
{ {
if (!(hdr->e_ident[EI_MAG0] == ELFMAG0 && if (!IS_ELF(*hdr) ||
hdr->e_ident[EI_MAG1] == ELFMAG1 && hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS ||
hdr->e_ident[EI_MAG2] == ELFMAG2 && hdr->e_ident[EI_DATA] != ELF_TARG_DATA ||
hdr->e_ident[EI_MAG3] == ELFMAG3)) hdr->e_ident[EI_VERSION] != EV_CURRENT)
return ENOEXEC; return ENOEXEC;
#ifdef __i386__ if (!ELF_MACHINE_OK(hdr->e_machine))
if (hdr->e_machine != EM_386 && hdr->e_machine != EM_486)
#endif
#ifdef __alpha__
if (hdr->e_machine != EM_ALPHA)
#endif
return ENOEXEC; return ENOEXEC;
if (hdr->e_type != type || hdr->e_version != ELF_TARG_VER)
if (hdr->e_type != type)
return ENOEXEC; return ENOEXEC;
return 0; return 0;
} }
static int static int
elf_load_section(struct vmspace *vmspace, struct vnode *vp, vm_offset_t offset, caddr_t vmaddr, size_t memsz, size_t filsz, vm_prot_t prot) elf_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)
{ {
size_t map_len; size_t map_len;
vm_offset_t map_addr; vm_offset_t map_addr;
int error; int error, rv;
unsigned char *data_buf = 0;
size_t copy_len; size_t copy_len;
vm_object_t object;
vm_offset_t file_addr;
vm_offset_t data_buf = 0;
object = vp->v_object;
error = 0;
map_addr = trunc_page((vm_offset_t)vmaddr); map_addr = trunc_page((vm_offset_t)vmaddr);
file_addr = trunc_page(offset);
if (memsz > filsz)
map_len = trunc_page(offset+filsz) - trunc_page(offset);
else
map_len = round_page(offset+filsz) - trunc_page(offset);
if (error = vm_mmap (&vmspace->vm_map,
&map_addr,
map_len,
prot,
VM_PROT_ALL,
MAP_PRIVATE | MAP_FIXED,
(caddr_t)vp,
trunc_page(offset)))
return error;
if (memsz == filsz)
return 0;
/* /*
* We have to map the remaining bit of the file into the kernel's * We have two choices. We can either clear the data in the last page
* memory map, allocate some anonymous memory, and copy that last * of an oversized mapping, or we can start the anon mapping a page
* bit into it. The remaining space should be .bss... * early and copy the initialized data into that first page. We
* choose the second..
*/
if (memsz > filsz)
map_len = trunc_page(offset+filsz) - file_addr;
else
map_len = round_page(offset+filsz) - file_addr;
if (map_len != 0) {
vm_object_reference(object);
vm_map_lock(&vmspace->vm_map);
rv = vm_map_insert(&vmspace->vm_map,
object,
file_addr, /* file offset */
map_addr, /* virtual start */
map_addr + map_len,/* virtual end */
prot,
VM_PROT_ALL,
MAP_COPY_NEEDED | MAP_COPY_ON_WRITE);
vm_map_unlock(&vmspace->vm_map);
if (rv != KERN_SUCCESS)
return EINVAL;
/* prefault the page tables */
pmap_object_init_pt(&vmspace->vm_pmap,
map_addr,
object,
(vm_pindex_t) OFF_TO_IDX(file_addr),
map_len,
0);
/* we can stop now if we've covered it all */
if (memsz == filsz)
return 0;
}
/*
* We have to get the remaining bit of the file into the first part
* of the oversized map segment. This is normally because the .data
* segment in the file is extended to provide bss. It's a neat idea
* to try and save a page, but it's a pain in the behind to implement.
*/ */
copy_len = (offset + filsz) - trunc_page(offset + filsz); copy_len = (offset + filsz) - trunc_page(offset + filsz);
map_addr = trunc_page((vm_offset_t)vmaddr + filsz); map_addr = trunc_page((vm_offset_t)vmaddr + filsz);
map_len = round_page((vm_offset_t)vmaddr + memsz) - map_addr; map_len = round_page((vm_offset_t)vmaddr + memsz) - map_addr;
/* This had damn well better be true! */
if (map_len != 0) { if (map_len != 0) {
if (error = vm_map_find(&vmspace->vm_map, NULL, 0, vm_map_lock(&vmspace->vm_map);
&map_addr, map_len, FALSE, rv = vm_map_insert(&vmspace->vm_map, NULL, 0,
VM_PROT_ALL, VM_PROT_ALL,0)) map_addr, map_addr + map_len,
return error; VM_PROT_ALL, VM_PROT_ALL, 0);
vm_map_unlock(&vmspace->vm_map);
if (rv != KERN_SUCCESS)
return EINVAL;
} }
if (error = vm_mmap(exec_map, if (copy_len != 0) {
(vm_offset_t *)&data_buf, vm_object_reference(object);
PAGE_SIZE, rv = vm_map_find(exec_map,
VM_PROT_READ, object,
VM_PROT_READ, trunc_page(offset + filsz),
0, &data_buf,
(caddr_t)vp, PAGE_SIZE,
trunc_page(offset + filsz))) TRUE,
return error; VM_PROT_READ,
VM_PROT_ALL,
MAP_COPY_ON_WRITE | MAP_COPY_NEEDED);
if (rv != KERN_SUCCESS) {
vm_object_deallocate(object);
return EINVAL;
}
pmap_object_init_pt(exec_map->pmap, data_buf, object,
(vm_pindex_t) OFF_TO_IDX(trunc_page(offset + filsz)),
PAGE_SIZE, 1);
error = copyout(data_buf, (caddr_t)map_addr, copy_len); /* send the page fragment to user space */
error = copyout((caddr_t)data_buf, (caddr_t)map_addr, copy_len);
vm_map_remove(exec_map, (vm_offset_t)data_buf, vm_map_remove(exec_map, data_buf, data_buf + PAGE_SIZE);
(vm_offset_t)data_buf + PAGE_SIZE); if (error)
return (error);
}
/* /*
* set it to the specified protection * set it to the specified protection
@ -250,7 +273,6 @@ elf_load_section(struct vmspace *vmspace, struct vnode *vp, vm_offset_t offset,
vm_map_protect(&vmspace->vm_map, map_addr, map_addr + map_len, prot, vm_map_protect(&vmspace->vm_map, map_addr, map_addr + map_len, prot,
FALSE); FALSE);
UPRINTF("bss size %d (%x)\n", map_len-copy_len, map_len-copy_len);
return error; return error;
} }
@ -263,10 +285,9 @@ elf_load_file(struct proc *p, char *file, u_long *addr, u_long *entry)
struct vmspace *vmspace = p->p_vmspace; struct vmspace *vmspace = p->p_vmspace;
struct vattr attr; struct vattr attr;
struct image_params image_params, *imgp; struct image_params image_params, *imgp;
vm_prot_t prot = 0; vm_prot_t prot;
unsigned long text_size = 0, data_size = 0; unsigned long text_size = 0, data_size = 0;
unsigned long text_addr = 0, data_addr = 0; unsigned long text_addr = 0, data_addr = 0;
int header_size = 0;
int error, i; int error, i;
imgp = &image_params; imgp = &image_params;
@ -312,19 +333,9 @@ elf_load_file(struct proc *p, char *file, u_long *addr, u_long *entry)
if (error = elf_check_header(hdr, ET_DYN)) if (error = elf_check_header(hdr, ET_DYN))
goto fail; goto fail;
/*
* ouch, need to bounds check in case user gives us a corrupted
* file with an insane header size
*/
if (hdr->e_phnum > MAX_PHDR) { /* XXX: ever more than this? */
error = ENOEXEC;
goto fail;
}
header_size = hdr->e_phentsize * hdr->e_phnum;
/* Only support headers that fit within first page for now */ /* Only support headers that fit within first page for now */
if (header_size + hdr->e_phoff > PAGE_SIZE) { if ((hdr->e_phoff > PAGE_SIZE) ||
(hdr->e_phoff + hdr->e_phentsize * hdr->e_phnum) > PAGE_SIZE) {
error = ENOEXEC; error = ENOEXEC;
goto fail; goto fail;
} }
@ -332,14 +343,8 @@ elf_load_file(struct proc *p, char *file, u_long *addr, u_long *entry)
phdr = (Elf_Phdr *)(imgp->image_header + hdr->e_phoff); phdr = (Elf_Phdr *)(imgp->image_header + hdr->e_phoff);
for (i = 0; i < hdr->e_phnum; i++) { for (i = 0; i < hdr->e_phnum; i++) {
switch(phdr[i].p_type) { if (phdr[i].p_type == PT_LOAD) { /* Loadable segment */
prot = 0;
case PT_NULL: /* NULL section */
UPRINTF ("ELF(file) PT_NULL section\n");
break;
case PT_LOAD: /* Loadable segment */
{
UPRINTF ("ELF(file) PT_LOAD section ");
if (phdr[i].p_flags & PF_X) if (phdr[i].p_flags & PF_X)
prot |= VM_PROT_EXECUTE; prot |= VM_PROT_EXECUTE;
if (phdr[i].p_flags & PF_W) if (phdr[i].p_flags & PF_W)
@ -347,7 +352,7 @@ elf_load_file(struct proc *p, char *file, u_long *addr, u_long *entry)
if (phdr[i].p_flags & PF_R) if (phdr[i].p_flags & PF_R)
prot |= VM_PROT_READ; prot |= VM_PROT_READ;
if (error = elf_load_section(vmspace, nd.ni_vp, if (error = elf_load_section(p, vmspace, nd.ni_vp,
phdr[i].p_offset, phdr[i].p_offset,
(caddr_t)phdr[i].p_vaddr + (caddr_t)phdr[i].p_vaddr +
(*addr), (*addr),
@ -367,37 +372,13 @@ elf_load_file(struct proc *p, char *file, u_long *addr, u_long *entry)
phdr[i].p_vaddr - phdr[i].p_vaddr -
trunc_page(phdr[i].p_vaddr)); trunc_page(phdr[i].p_vaddr));
*entry=(unsigned long)hdr->e_entry+(*addr); *entry=(unsigned long)hdr->e_entry+(*addr);
UPRINTF(".text <%08lx,%08lx> entry=%08lx\n",
text_addr, text_size, *entry);
} else { } else {
data_addr = trunc_page(phdr[i].p_vaddr+(*addr)); data_addr = trunc_page(phdr[i].p_vaddr+(*addr));
data_size = round_page(phdr[i].p_memsz + data_size = round_page(phdr[i].p_memsz +
phdr[i].p_vaddr - phdr[i].p_vaddr -
trunc_page(phdr[i].p_vaddr)); trunc_page(phdr[i].p_vaddr));
UPRINTF(".data <%08lx,%08lx>\n",
data_addr, data_size);
} }
} }
break;
case PT_DYNAMIC:/* Dynamic link information */
UPRINTF ("ELF(file) PT_DYNAMIC section\n");
break;
case PT_INTERP: /* Path to interpreter */
UPRINTF ("ELF(file) PT_INTERP section\n");
break;
case PT_NOTE: /* Note section */
UPRINTF ("ELF(file) PT_NOTE section\n");
break;
case PT_SHLIB: /* Shared lib section */
UPRINTF ("ELF(file) PT_SHLIB section\n");
break;
case PT_PHDR: /* Program header table info */
UPRINTF ("ELF(file) PT_PHDR section\n");
break;
default:
UPRINTF ("ELF(file) %d section ??\n", phdr[i].p_type );
}
} }
fail: fail:
@ -419,11 +400,11 @@ exec_elf_imgact(struct image_params *imgp)
const Elf_Phdr *phdr, *mapped_phdr = NULL; const Elf_Phdr *phdr, *mapped_phdr = NULL;
Elf_Auxargs *elf_auxargs = NULL; Elf_Auxargs *elf_auxargs = NULL;
struct vmspace *vmspace; struct vmspace *vmspace;
vm_prot_t prot = 0; vm_prot_t prot;
u_long text_size = 0, data_size = 0; u_long text_size = 0, data_size = 0;
u_long text_addr = 0, data_addr = 0; u_long text_addr = 0, data_addr = 0;
u_long addr, entry = 0, proghdr = 0; u_long addr, entry = 0, proghdr = 0;
int error, i, header_size = 0; int error, i;
const char *interp = NULL; const char *interp = NULL;
Elf_Brandinfo *brand_info; Elf_Brandinfo *brand_info;
char *brand; char *brand;
@ -440,24 +421,12 @@ exec_elf_imgact(struct image_params *imgp)
* detected an ELF file. * detected an ELF file.
*/ */
/*
* ouch, need to bounds check in case user gives us a corrupted
* file with an insane header size
*/
if (hdr->e_phnum > MAX_PHDR) { /* XXX: ever more than this? */
return ENOEXEC;
}
header_size = hdr->e_phentsize * hdr->e_phnum;
if ((hdr->e_phoff > PAGE_SIZE) || if ((hdr->e_phoff > PAGE_SIZE) ||
(hdr->e_phoff + header_size) > PAGE_SIZE) { (hdr->e_phoff + hdr->e_phentsize * hdr->e_phnum) > PAGE_SIZE) {
/* Only support headers in first page for now */ /* Only support headers in first page for now */
return ENOEXEC; return ENOEXEC;
} else {
phdr = (const Elf_Phdr*)
((const char *)imgp->image_header + hdr->e_phoff);
} }
phdr = (const Elf_Phdr*)(imgp->image_header + hdr->e_phoff);
/* /*
* From this point on, we may have resources that need to be freed. * From this point on, we may have resources that need to be freed.
@ -472,12 +441,8 @@ exec_elf_imgact(struct image_params *imgp)
for (i = 0; i < hdr->e_phnum; i++) { for (i = 0; i < hdr->e_phnum; i++) {
switch(phdr[i].p_type) { switch(phdr[i].p_type) {
case PT_NULL: /* NULL section */
UPRINTF ("ELF PT_NULL section\n");
break;
case PT_LOAD: /* Loadable segment */ case PT_LOAD: /* Loadable segment */
{ prot = 0;
UPRINTF ("ELF PT_LOAD section ");
if (phdr[i].p_flags & PF_X) if (phdr[i].p_flags & PF_X)
prot |= VM_PROT_EXECUTE; prot |= VM_PROT_EXECUTE;
if (phdr[i].p_flags & PF_W) if (phdr[i].p_flags & PF_W)
@ -485,7 +450,8 @@ exec_elf_imgact(struct image_params *imgp)
if (phdr[i].p_flags & PF_R) if (phdr[i].p_flags & PF_R)
prot |= VM_PROT_READ; prot |= VM_PROT_READ;
if (error = elf_load_section(vmspace, imgp->vp, if (error = elf_load_section(imgp->proc,
vmspace, imgp->vp,
phdr[i].p_offset, phdr[i].p_offset,
(caddr_t)phdr[i].p_vaddr, (caddr_t)phdr[i].p_vaddr,
phdr[i].p_memsz, phdr[i].p_memsz,
@ -504,44 +470,26 @@ exec_elf_imgact(struct image_params *imgp)
phdr[i].p_vaddr - phdr[i].p_vaddr -
text_addr); text_addr);
entry = (u_long)hdr->e_entry; entry = (u_long)hdr->e_entry;
UPRINTF(".text <%08lx,%08lx> entry=%08lx\n",
text_addr, text_size, entry);
} else { } else {
data_addr = trunc_page(phdr[i].p_vaddr); data_addr = trunc_page(phdr[i].p_vaddr);
data_size = round_page(phdr[i].p_memsz + data_size = round_page(phdr[i].p_memsz +
phdr[i].p_vaddr - phdr[i].p_vaddr -
data_addr); data_addr);
UPRINTF(".data <%08lx,%08lx>\n",
data_addr, data_size);
} }
}
break;
case PT_DYNAMIC:/* Dynamic link information */
UPRINTF ("ELF PT_DYNAMIC section ??\n");
break; break;
case PT_INTERP: /* Path to interpreter */ case PT_INTERP: /* Path to interpreter */
UPRINTF ("ELF PT_INTERP section ");
if (phdr[i].p_filesz > MAXPATHLEN || if (phdr[i].p_filesz > MAXPATHLEN ||
phdr[i].p_offset + phdr[i].p_filesz > PAGE_SIZE) { phdr[i].p_offset + phdr[i].p_filesz > PAGE_SIZE) {
error = ENOEXEC; error = ENOEXEC;
goto fail; goto fail;
} }
interp = imgp->image_header + phdr[i].p_offset; interp = imgp->image_header + phdr[i].p_offset;
UPRINTF("<%s>\n", interp);
break;
case PT_NOTE: /* Note section */
UPRINTF ("ELF PT_NOTE section\n");
break;
case PT_SHLIB: /* Shared lib section */
UPRINTF ("ELF PT_SHLIB section\n");
break; break;
case PT_PHDR: /* Program header table info */ case PT_PHDR: /* Program header table info */
UPRINTF ("ELF PT_PHDR section <%x>\n", phdr[i].p_vaddr);
proghdr = phdr[i].p_vaddr; proghdr = phdr[i].p_vaddr;
break; break;
default: default:
UPRINTF ("ELF %d section ??\n", phdr[i].p_type); break;
} }
} }
@ -602,7 +550,6 @@ exec_elf_imgact(struct image_params *imgp)
if (interp != NULL) { if (interp != NULL) {
strcpy(path, brand_info->emul_path); strcpy(path, brand_info->emul_path);
strcat(path, interp); strcat(path, interp);
UPRINTF("interpreter=<%s> %s\n", interp, brand_info->emul_path);
if ((error = elf_load_file(imgp->proc, path, &addr, if ((error = elf_load_file(imgp->proc, path, &addr,
&imgp->entry_addr)) != 0) { &imgp->entry_addr)) != 0) {
uprintf("ELF interpreter %s not found\n", path); uprintf("ELF interpreter %s not found\n", path);
@ -610,8 +557,6 @@ exec_elf_imgact(struct image_params *imgp)
} }
} }
UPRINTF("Executing %s binary\n", brand_info->brand);
/* /*
* Construct auxargs table (used by the fixup routine) * Construct auxargs table (used by the fixup routine)
*/ */