0cfff08e56
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
308 lines
7.3 KiB
C
308 lines
7.3 KiB
C
#define _SYSTEM 1
|
|
|
|
#include <minix/type.h>
|
|
#include <minix/const.h>
|
|
#include <minix/com.h>
|
|
#include <minix/syslib.h>
|
|
#include <sys/param.h>
|
|
#include <sys/mman.h>
|
|
#include <assert.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <libexec.h>
|
|
#include <string.h>
|
|
#include <machine/elf.h>
|
|
#include <machine/vmparam.h>
|
|
#include <machine/memory.h>
|
|
#include <minix/syslib.h>
|
|
|
|
/* For verbose logging */
|
|
#define ELF_DEBUG 0
|
|
|
|
/* Support only 32-bit ELF objects */
|
|
#define __ELF_WORD_SIZE 32
|
|
|
|
#define SECTOR_SIZE 512
|
|
|
|
static int check_header(Elf_Ehdr *hdr);
|
|
|
|
static int elf_sane(Elf_Ehdr *hdr)
|
|
{
|
|
if (check_header(hdr) != OK) {
|
|
return 0;
|
|
}
|
|
|
|
if((hdr->e_type != ET_EXEC) && (hdr->e_type != ET_DYN)) {
|
|
return 0;
|
|
}
|
|
|
|
if ((hdr->e_phoff > SECTOR_SIZE) ||
|
|
(hdr->e_phoff + hdr->e_phentsize * hdr->e_phnum) > SECTOR_SIZE) {
|
|
#if ELF_DEBUG
|
|
printf("peculiar phoff\n");
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int elf_ph_sane(Elf_Phdr *phdr)
|
|
{
|
|
if (rounddown((uintptr_t)phdr, sizeof(Elf_Addr)) != (uintptr_t)phdr) {
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int elf_unpack(char *exec_hdr,
|
|
int hdr_len, Elf_Ehdr **hdr, Elf_Phdr **phdr)
|
|
{
|
|
*hdr = (Elf_Ehdr *) exec_hdr;
|
|
if(!elf_sane(*hdr)) {
|
|
return ENOEXEC;
|
|
}
|
|
*phdr = (Elf_Phdr *)(exec_hdr + (*hdr)->e_phoff);
|
|
if(!elf_ph_sane(*phdr)) {
|
|
return ENOEXEC;
|
|
}
|
|
#if 0
|
|
if((int)((*phdr) + (*hdr)->e_phnum) >= hdr_len) return ENOEXEC;
|
|
#endif
|
|
return OK;
|
|
}
|
|
|
|
#define IS_ELF(ehdr) ((ehdr).e_ident[EI_MAG0] == ELFMAG0 && \
|
|
(ehdr).e_ident[EI_MAG1] == ELFMAG1 && \
|
|
(ehdr).e_ident[EI_MAG2] == ELFMAG2 && \
|
|
(ehdr).e_ident[EI_MAG3] == ELFMAG3)
|
|
|
|
static int check_header(Elf_Ehdr *hdr)
|
|
{
|
|
if (!IS_ELF(*hdr) ||
|
|
hdr->e_ident[EI_DATA] != ELF_TARG_DATA ||
|
|
hdr->e_ident[EI_VERSION] != EV_CURRENT ||
|
|
hdr->e_phentsize != sizeof(Elf_Phdr) ||
|
|
hdr->e_version != ELF_TARG_VER)
|
|
return ENOEXEC;
|
|
|
|
return OK;
|
|
}
|
|
|
|
/* Return >0 if there is an ELF interpreter (i.e. it is a dynamically linked
|
|
* executable) and we could extract it successfully.
|
|
* Return 0 if there isn't one.
|
|
* Return <0 on error.
|
|
*/
|
|
int elf_has_interpreter(char *exec_hdr, /* executable header */
|
|
int hdr_len, char *interp, int maxsz)
|
|
{
|
|
Elf_Ehdr *hdr = NULL;
|
|
Elf_Phdr *phdr = NULL;
|
|
int e, i;
|
|
|
|
if((e=elf_unpack(exec_hdr, hdr_len, &hdr, &phdr)) != OK) return 0;
|
|
|
|
for (i = 0; i < hdr->e_phnum; i++) {
|
|
switch (phdr[i].p_type) {
|
|
case PT_INTERP:
|
|
if(!interp) return 1;
|
|
if(phdr[i].p_filesz >= maxsz)
|
|
return -1;
|
|
if(phdr[i].p_offset + phdr[i].p_filesz >= hdr_len)
|
|
return -1;
|
|
memcpy(interp, exec_hdr + phdr[i].p_offset, phdr[i].p_filesz);
|
|
interp[phdr[i].p_filesz] = '\0';
|
|
return 1;
|
|
default:
|
|
continue;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int libexec_load_elf(struct exec_info *execi)
|
|
{
|
|
Elf_Ehdr *hdr = NULL;
|
|
Elf_Phdr *phdr = NULL;
|
|
int e, i = 0;
|
|
int first = 1;
|
|
vir_bytes startv = 0, stacklow;
|
|
|
|
assert(execi != NULL);
|
|
assert(execi->hdr != NULL);
|
|
|
|
if((e=elf_unpack(execi->hdr, execi->hdr_len, &hdr, &phdr)) != OK) {
|
|
return e;
|
|
}
|
|
|
|
/* this function can load the dynamic linker, but that
|
|
* shouldn't require an interpreter itself.
|
|
*/
|
|
i = elf_has_interpreter(execi->hdr, execi->hdr_len, NULL, 0);
|
|
if(i > 0) {
|
|
return ENOEXEC;
|
|
}
|
|
|
|
execi->stack_size = roundup(execi->stack_size, PAGE_SIZE);
|
|
execi->stack_high = rounddown(execi->stack_high, PAGE_SIZE);
|
|
stacklow = execi->stack_high - execi->stack_size;
|
|
|
|
assert(execi->copymem);
|
|
assert(execi->clearmem);
|
|
assert(execi->allocmem_prealloc_cleared);
|
|
assert(execi->allocmem_prealloc_junk);
|
|
assert(execi->allocmem_ondemand);
|
|
|
|
for (i = 0; i < hdr->e_phnum; i++) {
|
|
Elf_Phdr *ph = &phdr[i];
|
|
off_t file_limit = ph->p_offset + ph->p_filesz;
|
|
/* sanity check binary before wiping out the target process */
|
|
if(execi->filesize < file_limit) {
|
|
return ENOEXEC;
|
|
}
|
|
}
|
|
|
|
if(execi->clearproc) execi->clearproc(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;
|
|
|
|
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
|
|
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("mmapped 0x%lx-0x%lx\n", vaddr, vaddr+seg_membytes);
|
|
#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 ELF_DEBUG
|
|
printf("copied 0x%lx-0x%lx\n", p_vaddr, p_vaddr+ph->p_filesz);
|
|
#endif
|
|
|
|
/* 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);
|
|
#endif
|
|
execi->clearmem(execi, vfileend, chunk);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Make it a stack */
|
|
if(execi->allocmem_ondemand(execi, stacklow, execi->stack_size) != OK) {
|
|
if(execi->clearproc) execi->clearproc(execi);
|
|
return ENOMEM;
|
|
}
|
|
|
|
#if ELF_DEBUG
|
|
printf("stack mmapped 0x%lx-0x%lx\n", stacklow, stacklow+execi->stack_size);
|
|
#endif
|
|
|
|
/* record entry point and lowest load vaddr for caller */
|
|
execi->pc = hdr->e_entry + execi->load_offset;
|
|
execi->load_base = startv;
|
|
|
|
return OK;
|
|
}
|
|
|