libexec: mmap support, prealloc variants

In libexec, split the memory allocation method into cleared and
non-cleared. Cleared gives zeroed memory, non-cleared gives 'junk'
memory (that will be overwritten anyway, and so needn't be cleared)
that is faster to get.

Also introduce the 'memmap' method that can be used, if available,
to map code and data from executables into a process using the
third-party mmap() mode.

Change-Id: I26694fd3c21deb8b97e01ed675dfc14719b0672b
This commit is contained in:
Ben Gras 2013-03-16 03:46:37 +00:00
parent 6fa5ce74ce
commit 0cfff08e56
8 changed files with 129 additions and 32 deletions

View file

@ -138,7 +138,8 @@ void arch_boot_proc(struct boot_image *ip, struct proc *rp)
/* callbacks for use in the kernel */
execi.copymem = libexec_copy_memcpy;
execi.clearmem = libexec_clear_memset;
execi.allocmem_prealloc = libexec_pg_alloc;
execi.allocmem_prealloc_cleared = libexec_pg_alloc;
execi.allocmem_prealloc_junk = libexec_pg_alloc;
execi.allocmem_ondemand = libexec_pg_alloc;
execi.clearproc = NULL;

View file

@ -414,7 +414,8 @@ void arch_boot_proc(struct boot_image *ip, struct proc *rp)
/* callbacks for use in the kernel */
execi.copymem = libexec_copy_memcpy;
execi.clearmem = libexec_clear_memset;
execi.allocmem_prealloc = libexec_pg_alloc;
execi.allocmem_prealloc_junk = libexec_pg_alloc;
execi.allocmem_prealloc_cleared = libexec_pg_alloc;
execi.allocmem_ondemand = libexec_pg_alloc;
execi.clearproc = NULL;

View file

@ -150,7 +150,8 @@ int libexec_load_elf(struct exec_info *execi)
assert(execi->copymem);
assert(execi->clearmem);
assert(execi->allocmem_prealloc);
assert(execi->allocmem_prealloc_cleared);
assert(execi->allocmem_prealloc_junk);
assert(execi->allocmem_ondemand);
for (i = 0; i < hdr->e_phnum; i++) {
@ -167,51 +168,124 @@ int libexec_load_elf(struct exec_info *execi)
for (i = 0; i < hdr->e_phnum; i++) {
vir_bytes seg_membytes, page_offset, p_vaddr, vaddr;
vir_bytes chunk, vfileend, vmemend;
off_t foffset, fbytes;
Elf_Phdr *ph = &phdr[i];
int try_mmap = 1;
u16_t clearend = 0;
int pagechunk;
int mmap_prot = PROT_READ;
if(!(ph->p_flags & PF_R)) {
printf("libexec: warning: unreadable segment\n");
}
if(ph->p_flags & PF_W) {
mmap_prot |= PROT_WRITE;
#if ELF_DEBUG
printf("libexec: adding PROT_WRITE\n");
#endif
} else {
#if ELF_DEBUG
printf("libexec: not adding PROT_WRITE\n");
#endif
}
if (ph->p_type != PT_LOAD || ph->p_memsz == 0) continue;
if((ph->p_vaddr % PAGE_SIZE) != (ph->p_offset % PAGE_SIZE)) {
printf("libexec: unaligned ELF program?\n");
try_mmap = 0;
}
if(!execi->memmap) {
try_mmap = 0;
}
foffset = ph->p_offset;
fbytes = ph->p_filesz;
vaddr = p_vaddr = ph->p_vaddr + execi->load_offset;
seg_membytes = ph->p_memsz;
page_offset = vaddr % PAGE_SIZE;
vaddr -= page_offset;
foffset -= page_offset;
seg_membytes += page_offset;
fbytes += page_offset;
vfileend = p_vaddr + ph->p_filesz;
/* if there's usable memory after the file end, we have
* to tell VM to clear the memory part of the page when it's
* mapped in
*/
if((pagechunk = (vfileend % PAGE_SIZE))
&& ph->p_filesz < ph->p_memsz) {
clearend = PAGE_SIZE - pagechunk;
}
seg_membytes = roundup(seg_membytes, PAGE_SIZE);
fbytes = roundup(fbytes, PAGE_SIZE);
if(first || startv > vaddr) startv = vaddr;
first = 0;
/* make us some memory */
if(execi->allocmem_prealloc(execi, vaddr, seg_membytes) != OK) {
if(execi->clearproc) execi->clearproc(execi);
return ENOMEM;
}
if(try_mmap && execi->memmap(execi, vaddr, fbytes, foffset, clearend, mmap_prot) == OK) {
#if ELF_DEBUG
printf("mmapped 0x%lx-0x%lx\n", vaddr, vaddr+seg_membytes);
printf("libexec: mmap 0x%lx-0x%lx done, clearend 0x%x\n",
vaddr, vaddr+fbytes, clearend);
#endif
/* Copy executable section into it */
if(execi->copymem(execi, ph->p_offset, p_vaddr, ph->p_filesz) != OK) {
if(execi->clearproc) execi->clearproc(execi);
return ENOMEM;
}
if(seg_membytes > fbytes) {
int rem_mem = seg_membytes - fbytes;;
vir_bytes remstart = vaddr + fbytes;
if(execi->allocmem_ondemand(execi,
remstart, rem_mem) != OK) {
printf("libexec: mmap extra mem failed\n");
return ENOMEM;
}
#if ELF_DEBUG
else printf("libexec: allocated 0x%lx-0x%lx\n",
remstart, remstart+rem_mem);
#endif
}
} else {
if(try_mmap) printf("libexec: mmap failed\n");
/* make us some memory */
if(execi->allocmem_prealloc_junk(execi, vaddr, seg_membytes) != OK) {
if(execi->clearproc) execi->clearproc(execi);
return ENOMEM;
}
#if ELF_DEBUG
printf("copied 0x%lx-0x%lx\n", p_vaddr, p_vaddr+ph->p_filesz);
printf("mmapped 0x%lx-0x%lx\n", vaddr, vaddr+seg_membytes);
#endif
/* Clear remaining bits */
vfileend = p_vaddr + ph->p_filesz;
vmemend = vaddr + seg_membytes;
if((chunk = p_vaddr - vaddr) > 0) {
/* Copy executable section into it */
if(execi->copymem(execi, ph->p_offset, p_vaddr, ph->p_filesz) != OK) {
if(execi->clearproc) execi->clearproc(execi);
return ENOMEM;
}
#if ELF_DEBUG
printf("start clearing 0x%lx-0x%lx\n", vaddr, vaddr+chunk);
printf("copied 0x%lx-0x%lx\n", p_vaddr, p_vaddr+ph->p_filesz);
#endif
execi->clearmem(execi, vaddr, chunk);
}
if((chunk = vmemend - vfileend) > 0) {
/* Clear remaining bits */
vmemend = vaddr + seg_membytes;
if((chunk = p_vaddr - vaddr) > 0) {
#if ELF_DEBUG
printf("end clearing 0x%lx-0x%lx\n", vfileend, vaddr+chunk);
printf("start clearing 0x%lx-0x%lx\n", vaddr, vaddr+chunk);
#endif
execi->clearmem(execi, vfileend, chunk);
execi->clearmem(execi, vaddr, chunk);
}
if((chunk = vmemend - vfileend) > 0) {
#if ELF_DEBUG
printf("end clearing 0x%lx-0x%lx\n", vfileend, vaddr+chunk);
#endif
execi->clearmem(execi, vfileend, chunk);
}
}
}

View file

@ -18,7 +18,7 @@
#include <sys/mman.h>
#include <machine/elf.h>
int libexec_alloc_mmap_prealloc(struct exec_info *execi, off_t vaddr, size_t len)
int libexec_alloc_mmap_prealloc_junk(struct exec_info *execi, off_t vaddr, size_t len)
{
if(minix_mmap_for(execi->proc_e, (void *) vaddr, len,
PROT_READ|PROT_WRITE|PROT_EXEC,
@ -29,6 +29,17 @@ int libexec_alloc_mmap_prealloc(struct exec_info *execi, off_t vaddr, size_t len
return OK;
}
int libexec_alloc_mmap_prealloc_cleared(struct exec_info *execi, off_t vaddr, size_t len)
{
if(minix_mmap_for(execi->proc_e, (void *) vaddr, len,
PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_ANON|MAP_PREALLOC|MAP_FIXED, -1, 0) == MAP_FAILED) {
return ENOMEM;
}
return OK;
}
int libexec_alloc_mmap_ondemand(struct exec_info *execi, off_t vaddr, size_t len)
{
if(minix_mmap_for(execi->proc_e, (void *) vaddr, len,

View file

@ -16,6 +16,10 @@ typedef int (*libexec_allocfunc_t)(struct exec_info *execi,
typedef int (*libexec_procclearfunc_t)(struct exec_info *execi);
typedef int (*libexec_mmap_t)(struct exec_info *execi,
vir_bytes vaddr, vir_bytes len, vir_bytes foffset, u16_t clearend,
int protflags);
struct exec_info {
/* Filled in by libexec caller */
endpoint_t proc_e; /* Process endpoint */
@ -33,9 +37,11 @@ struct exec_info {
/* Callback pointers for use by libexec */
libexec_loadfunc_t copymem; /* Copy callback */
libexec_clearfunc_t clearmem; /* Clear callback */
libexec_allocfunc_t allocmem_prealloc; /* Alloc callback */
libexec_allocfunc_t allocmem_prealloc_cleared; /* Alloc callback */
libexec_allocfunc_t allocmem_prealloc_junk; /* Alloc callback */
libexec_allocfunc_t allocmem_ondemand; /* Alloc callback */
libexec_procclearfunc_t clearproc; /* Clear process callback */
libexec_mmap_t memmap; /* mmap callback */
void *opaque; /* Callback data */
/* Filled in by libexec load function */
@ -55,7 +61,8 @@ int libexec_load_elf(struct exec_info *execi);
int libexec_copy_memcpy(struct exec_info *execi, off_t offset, off_t vaddr, size_t len);
int libexec_clear_memset(struct exec_info *execi, off_t vaddr, size_t len);
int libexec_alloc_mmap_prealloc(struct exec_info *execi, off_t vaddr, size_t len);
int libexec_alloc_mmap_prealloc_cleared(struct exec_info *execi, off_t vaddr, size_t len);
int libexec_alloc_mmap_prealloc_junk(struct exec_info *execi, off_t vaddr, size_t len);
int libexec_alloc_mmap_ondemand(struct exec_info *execi, off_t vaddr, size_t len);
int libexec_clearproc_vm_procctl(struct exec_info *execi);
int libexec_clear_sys_memset(struct exec_info *execi, off_t vaddr, size_t len);

View file

@ -133,7 +133,8 @@ static int do_exec(int proc_e, char *exec, size_t exec_len, char *progname,
execi.copymem = read_seg;
execi.clearproc = libexec_clearproc_vm_procctl;
execi.clearmem = libexec_clear_sys_memset;
execi.allocmem_prealloc = libexec_alloc_mmap_prealloc;
execi.allocmem_prealloc_cleared = libexec_alloc_mmap_prealloc_cleared;
execi.allocmem_prealloc_junk = libexec_alloc_mmap_prealloc_junk;
execi.allocmem_ondemand = libexec_alloc_mmap_ondemand;
for(i = 0; exec_loaders[i].load_object != NULL; i++) {

View file

@ -306,7 +306,8 @@ int pm_exec(endpoint_t proc_e, vir_bytes path, size_t path_len,
execi.args.copymem = read_seg;
execi.args.clearproc = libexec_clearproc_vm_procctl;
execi.args.clearmem = libexec_clear_sys_memset;
execi.args.allocmem_prealloc = libexec_alloc_mmap_prealloc;
execi.args.allocmem_prealloc_cleared = libexec_alloc_mmap_prealloc_cleared;
execi.args.allocmem_prealloc_junk = libexec_alloc_mmap_prealloc_junk;
execi.args.allocmem_ondemand = libexec_alloc_mmap_ondemand;
execi.args.opaque = &execi;

View file

@ -287,7 +287,8 @@ void exec_bootproc(struct vmproc *vmp, struct boot_image *ip)
execi->copymem = libexec_copy_physcopy;
execi->clearproc = NULL;
execi->clearmem = libexec_clear_sys_memset;
execi->allocmem_prealloc = libexec_alloc_vm_prealloc;
execi->allocmem_prealloc_junk = libexec_alloc_vm_prealloc;
execi->allocmem_prealloc_cleared = libexec_alloc_vm_prealloc;
execi->allocmem_ondemand = libexec_alloc_vm_ondemand;
if(libexec_load_elf(execi) != OK)