From 0cfff08e561c56297989976f4e3e37b4c83d5d7d Mon Sep 17 00:00:00 2001 From: Ben Gras Date: Sat, 16 Mar 2013 03:46:37 +0000 Subject: [PATCH] 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 --- kernel/arch/earm/protect.c | 3 +- kernel/arch/i386/protect.c | 3 +- lib/libexec/exec_elf.c | 120 ++++++++++++++++++++++++++++++------- lib/libexec/exec_general.c | 13 +++- lib/libexec/libexec.h | 11 +++- servers/rs/exec.c | 3 +- servers/vfs/exec.c | 3 +- servers/vm/main.c | 3 +- 8 files changed, 128 insertions(+), 31 deletions(-) diff --git a/kernel/arch/earm/protect.c b/kernel/arch/earm/protect.c index fe6df33d9..75b3ef897 100644 --- a/kernel/arch/earm/protect.c +++ b/kernel/arch/earm/protect.c @@ -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; diff --git a/kernel/arch/i386/protect.c b/kernel/arch/i386/protect.c index 22acc3350..5a98a41ad 100644 --- a/kernel/arch/i386/protect.c +++ b/kernel/arch/i386/protect.c @@ -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; diff --git a/lib/libexec/exec_elf.c b/lib/libexec/exec_elf.c index 625b3f087..24bfed966 100644 --- a/lib/libexec/exec_elf.c +++ b/lib/libexec/exec_elf.c @@ -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("libexec: mmap 0x%lx-0x%lx done, clearend 0x%x\n", + vaddr, vaddr+fbytes, clearend); +#endif + 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 - printf("mmapped 0x%lx-0x%lx\n", vaddr, vaddr+seg_membytes); + else printf("libexec: allocated 0x%lx-0x%lx\n", + + remstart, remstart+rem_mem); #endif + } + } else { + if(try_mmap) printf("libexec: mmap failed\n"); - /* 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; - } + /* 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("start clearing 0x%lx-0x%lx\n", vaddr, vaddr+chunk); +#endif + execi->clearmem(execi, vaddr, chunk); + } + + if((chunk = vmemend - vfileend) > 0) { #if ELF_DEBUG - printf("end clearing 0x%lx-0x%lx\n", vfileend, vaddr+chunk); + printf("end clearing 0x%lx-0x%lx\n", vfileend, vaddr+chunk); #endif - execi->clearmem(execi, vfileend, chunk); + execi->clearmem(execi, vfileend, chunk); + } } } diff --git a/lib/libexec/exec_general.c b/lib/libexec/exec_general.c index 2ac942df9..a1afecfa7 100644 --- a/lib/libexec/exec_general.c +++ b/lib/libexec/exec_general.c @@ -18,7 +18,7 @@ #include #include -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, diff --git a/lib/libexec/libexec.h b/lib/libexec/libexec.h index 8b6db7618..8597e74c3 100644 --- a/lib/libexec/libexec.h +++ b/lib/libexec/libexec.h @@ -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); diff --git a/servers/rs/exec.c b/servers/rs/exec.c index d0c950f67..ffbab2c78 100644 --- a/servers/rs/exec.c +++ b/servers/rs/exec.c @@ -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++) { diff --git a/servers/vfs/exec.c b/servers/vfs/exec.c index daad6a2fb..ce00e56c8 100644 --- a/servers/vfs/exec.c +++ b/servers/vfs/exec.c @@ -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; diff --git a/servers/vm/main.c b/servers/vm/main.c index 9cd14b444..caf148de5 100644 --- a/servers/vm/main.c +++ b/servers/vm/main.c @@ -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) -- 2.44.0