diff --git a/proc/process.c b/proc/process.c index 0b06c526..b5c245b4 100644 --- a/proc/process.c +++ b/proc/process.c @@ -602,7 +602,7 @@ int process_load32(vm_map_t *map, vm_object_t *o, off_t base, void *iehdr, size_ } -int process_load64(vm_map_t *map, vm_object_t *o, off_t base, void *iehdr, size_t size, size_t *ustacksz, hal_tls_t *tls, ptr_t *tbssAddr) +int process_load64(vm_map_t *map, vm_object_t *o, off_t base, void *iehdr, size_t size, size_t *ustacksz, hal_tls_t *tls, ptr_t *tbssAddr, struct auxInfo **auxData) { void *vaddr; size_t memsz, filesz; @@ -617,6 +617,16 @@ int process_load64(vm_map_t *map, vm_object_t *o, off_t base, void *iehdr, size_ return -ENOEXEC; } + (*auxData)->a_type = AT_ENTRY; + (*auxData)->a_v = (u64)ehdr->e_entry; + (*auxData)++; + (*auxData)->a_type = AT_PHNUM; + (*auxData)->a_v = (u64)ehdr->e_phnum; + (*auxData)++; + (*auxData)->a_type = AT_PHENT; + (*auxData)->a_v = (u64)ehdr->e_phentsize; + (*auxData)++; + shdr = (void *)((char *)ehdr + ehdr->e_shoff); shstrshdr = shdr + ehdr->e_shstrndx; snameTab = (char *)ehdr + shstrshdr->sh_offset; @@ -640,6 +650,12 @@ int process_load64(vm_map_t *map, vm_object_t *o, off_t base, void *iehdr, size_ *ustacksz = round_page(phdr->p_memsz); } + if (phdr->p_type == PT_PHDR) { + (*auxData)->a_type = AT_PHDR; + (*auxData)->a_v = (u64)phdr->p_vaddr; + (*auxData)++; + } + if ((phdr->p_type != PT_LOAD) || (phdr->p_vaddr == 0)) { continue; } @@ -685,6 +701,188 @@ int process_load64(vm_map_t *map, vm_object_t *o, off_t base, void *iehdr, size_ } +int process_getInterpreterPath64(const Elf64_Ehdr *ehdr, const char **interpreterPath) +{ + const Elf64_Phdr *phdrs = (const void *)ehdr + ehdr->e_phoff; + Elf64_Half i; + Elf64_Word letter; + + *interpreterPath = NULL; + + for (i = 0; i < ehdr->e_phnum; i++) { + if (phdrs[i].p_type != PT_INTERP) { + continue; + } + + if (phdrs[i].p_filesz == 0) { + return -EINVAL; + } + + *interpreterPath = (const char *)ehdr + phdrs[i].p_offset; + /* Check that interpreter path is of specified length. */ + for (letter = 0; letter < phdrs[i].p_filesz; letter++) { + if ((*interpreterPath)[letter] == '\0') { + break; + } + } + if (letter != (phdrs[i].p_filesz - 1)) { + return -EINVAL; + } + } + + return EOK; +} + + +int process_doLoadInterpreter64(vm_map_t *map, vm_object_t *o, off_t base, void *iehdr, size_t size, void **baseAddr, void **interpreterEntry) +{ + void *vaddr; + size_t memsz, filesz; + Elf64_Ehdr *ehdr = iehdr; + Elf64_Phdr *phdrs = (void *)ehdr + ehdr->e_phoff; + unsigned i, prot, flags; + Elf64_Off offs, misalign; + Elf64_Word al; + Elf64_Addr loadLow, loadHigh; + size_t loadSize; + + if (process_validateElf64(iehdr, size) < 0) { + return -ENOEXEC; + } + + /* Dynamic linker needs to be mapped in a contignous part of memory. */ + /* Get lowest and highest loaded address to to obtain total size. */ + loadLow = ~(Elf64_Addr)0; + loadHigh = 0; + for (i = 0; i < ehdr->e_phnum; i++) { + if (phdrs[i].p_type == PT_LOAD) { + loadLow = min(loadLow, phdrs[i].p_vaddr); + loadHigh = max(loadHigh, phdrs[i].p_vaddr + phdrs[i].p_memsz); + } + } + + /* No loadable section found. */ + if (loadHigh < loadLow) { + return -ENOEXEC; + } + /* Find a place in vm where interpreter will fit. */ + loadSize = round_page(loadHigh - loadLow); + *baseAddr = vm_mmap(map, (void *)(ptr_t)loadLow, NULL, loadSize, PROT_USER, NULL, -1, MAP_NONE); + if ((*baseAddr) == NULL) { + return -ENOMEM; + } + if (vm_munmap(map, *baseAddr, loadSize) < 0) { + return -ENOMEM; + } + + for (i = 0; i < ehdr->e_phnum; i++) { + if (phdrs[i].p_type != PT_LOAD) { + continue; + } + + al = max(phdrs[i].p_align, 1); + vaddr = ((char *)*baseAddr) + ((phdrs[i].p_vaddr & ~(al - 1)) - loadLow); + offs = phdrs[i].p_offset & ~(al - 1); + misalign = phdrs[i].p_offset & (al - 1); + filesz = (phdrs[i].p_filesz != 0) ? (phdrs[i].p_filesz + misalign) : 0; + memsz = phdrs[i].p_memsz + misalign; + + prot = PROT_USER; + flags = MAP_FIXED; + + if ((phdrs[i].p_flags & PF_R) != 0) { + prot |= PROT_READ; + } + + if ((phdrs[i].p_flags & PF_W) != 0) { + prot |= PROT_WRITE; + } + + if ((phdrs[i].p_flags & PF_X) != 0) { + prot |= PROT_EXEC; + } + + if (filesz != 0) { + if ((prot & PROT_WRITE) != 0) { + flags |= MAP_NEEDSCOPY; + } + if (vm_mmap(map, vaddr, NULL, round_page(filesz), prot, o, base + offs, flags) == NULL) { + return -ENOMEM; + } + } + + if (filesz != memsz) { + if (round_page(memsz) != round_page(filesz)) { + if (vm_mmap(map, vaddr + round_page(filesz), NULL, round_page(memsz) - round_page(filesz), prot, NULL, -1, MAP_FIXED) == NULL) { + return -ENOMEM; + } + } + hal_memset(vaddr + filesz, 0, round_page((ptr_t)vaddr + memsz) - ((ptr_t)vaddr + filesz)); + } + } + + *interpreterEntry = ((char *)(*baseAddr) + (ehdr->e_entry - loadLow)); + + return EOK; +} + + +/* *interpreterEntry is changed only if interpreter is found and loaded properly. */ +int process_loadInterpreter64(vm_map_t *map, vm_object_t *o, off_t base, const void *iehdr, size_t size, void **interpreterEntry, struct auxInfo **auxData, int *hasInterpreter) +{ + const Elf64_Ehdr *ehdr = iehdr; + int err; + const char *interpreterPath; + oid_t interpOid; + vm_object_t *interpO; + off_t interpBase = 0; + void *interpIehdr; + size_t interpSize; + void *baseAddr; + + err = process_getInterpreterPath64(ehdr, &interpreterPath); + if (err < 0) { + return err; + } + if (interpreterPath == NULL) { + return EOK; + } + + /* Load interpreter. */ + err = proc_lookup(interpreterPath, NULL, &interpOid); + if (err < 0) { + return err; + } + + err = vm_objectGet(&interpO, interpOid); + if (err < 0) { + return err; + } + interpSize = round_page(interpO->size); + interpIehdr = vm_mmap(process_common.kmap, NULL, NULL, interpSize, PROT_READ, interpO, interpBase, MAP_NONE); + if (interpIehdr == NULL) { + vm_objectPut(interpO); + return -ENOMEM; + } + err = process_doLoadInterpreter64(map, interpO, interpBase, interpIehdr, interpSize, &baseAddr, interpreterEntry); + + vm_munmap(process_common.kmap, interpIehdr, interpSize); + vm_objectPut(interpO); + + if (err < 0) { + return err; + } + + *hasInterpreter = 1; + + (*auxData)->a_type = AT_BASE; + (*auxData)->a_v = (u64)(ptr_t)baseAddr; + (*auxData)++; + + return EOK; +} + + int process_getInterpreterPath32(const Elf32_Ehdr *ehdr, const char **interpreterPath) { const Elf32_Phdr *phdrs = (const void *)ehdr + ehdr->e_phoff; @@ -901,8 +1099,10 @@ int process_load(process_t *process, vm_object_t *o, off_t base, size_t size, vo case ELFCLASS64: *entry = (void *)(ptr_t)ehdr->e_entry; - err = process_load64(map, o, base, ehdr, size, &ustacksz, &tlsNew, &tbssAddr); - /* FIXME: loading interpreter on 64 bit ELFs is not supported */ + err = process_load64(map, o, base, ehdr, size, &ustacksz, &tlsNew, &tbssAddr, auxData); + if (err == 0) { + err = process_loadInterpreter64(map, o, base, ehdr, size, entry, auxData, &process->hasInterpreter); + } break; default: err = -ENOEXEC;