#include #include #include #include #include #include typedef struct{ uint64_t a_type; uint64_t a_val; } auxv64_t; typedef struct { auxv64_t phdr; auxv64_t null; } auxv64_list; static size_t phflagtommuflag(size_t phflags){ size_t mmuflags = 0; if(phflags & ELF_FLAG_EXECUTABLE == 0) mmuflags |= ARCH_MMU_MAP_NOEXEC; if(phflags & ELF_FLAG_WRITABLE) mmuflags |= ARCH_MMU_MAP_WRITE; if(phflags & ELF_FLAG_READABLE) mmuflags |= ARCH_MMU_MAP_READ; return mmuflags | ARCH_MMU_MAP_USER; } static bool check_header64(elf_header64* header){ // XXX throw this into arch if(header->magic != ELF_MAGIC) return false; if(header->bits != ELF_BITS_64) return false; if(header->endianness != ELF_ENDIANNESS_LITTLE) return false; if(header->isa != ELF_ISA_X86_64) return false; return true; } static void* stacksetup(char* interp, char** argv, char** env, elf_header64 header, elf_ph64* phs){ size_t argc = 0; size_t envc = 0; size_t initialsize = 0; size_t argdatasize = 0; size_t envdatasize = 0; size_t auxvsize = 5*16; // FIXME better way of keeping track of this while(argv[argc]){ argdatasize += strlen(argv[argc]) + 1; initialsize += strlen(argv[argc]) + 1; ++argc; } while(env[envc]){ envdatasize += strlen(env[envc]) + 1; initialsize += strlen(env[envc]) + 1; ++envc; } if(interp){ argdatasize += strlen(interp) + 1; initialsize += strlen(interp) + 1; initialsize += sizeof(char*); // interpreter ptr } // pointers to args and envs initialsize += argc*sizeof(char*); //args initialsize += envc*sizeof(char*); //envs initialsize += sizeof(size_t); //argc // allocate memory for the initial section of the stack // XXX demand alloc the non initialised stack pages if(!vmm_allocnowat(STACK_TOP - initialsize - PAGE_SIZE*200, ARCH_MMU_MAP_READ | ARCH_MMU_MAP_WRITE | ARCH_MMU_MAP_NOEXEC | ARCH_MMU_MAP_USER, initialsize + PAGE_SIZE*200)) return NULL; // get the addresses to copy the data to char* argdatastart = STACK_TOP - argdatasize; char* envdatastart = argdatastart - envdatasize; auxv64_t* auxvstart = (void*)(((uintptr_t)envdatastart & (~0xF)) - auxvsize); // count + 1 because of a null entry char** envstart = (void*)auxvstart - (envc + 1)*sizeof(char*); char** argstart = (void*)envstart - (argc + 1)*sizeof(char*); if(interp) --argstart; size_t* argcptr = (void*)argstart - sizeof(size_t); // auxv // AT_PHDR auxvstart->a_type = AT_PHDR; auxvstart->a_val = (uint64_t)phs; auxvstart++; // AT_PHENT auxvstart->a_type = AT_PHENT; auxvstart->a_val = header.ph_size; auxvstart++; // AT_PHNUM auxvstart->a_type = AT_PHNUM; auxvstart->a_val = header.ph_count; auxvstart++; // AT_ENTRY auxvstart->a_type = AT_ENTRY; auxvstart->a_val = header.entry; auxvstart++; // AT_NULL auxvstart->a_type = AT_NULL; auxvstart->a_val = 0; // copy the data if(interp){ strcpy(argdatastart, interp); *argstart++ = argdatastart; argdatastart += strlen(interp) + 1; } for(size_t i = 0; i < argc; ++i){ strcpy(argdatastart, argv[i]); *argstart++ = argdatastart; argdatastart += strlen(argv[i]) + 1; } *argstart = NULL; for(size_t i = 0; i < envc; ++i){ strcpy(envdatastart, env[i]); *envstart++ = envdatastart; envdatastart += strlen(env[i]) + 1; } *envstart = NULL; if(interp) argc++; *argcptr = argc; return argcptr; } int elf_load(thread_t* thread, vnode_t* node, char** argv, char** env){ elf_header64 header; elf_header64 progheader; vnode_t* prognode = node; elf_ph64 ph; int err = 0; int readc = vfs_read(&err, node, &header, sizeof(elf_header64), 0); char* interp = NULL; if(err) return err; if(readc != sizeof(elf_header64) || (!check_header64(&header))) return EINVAL; progheader = header; // incase an interpreter exists // find out if this needs an interpreter // and also save the executable's program headers // for auxv elf_ph64* progphs; for(size_t currentph = 0; currentph < header.ph_count; ++currentph){ readc = vfs_read(&err, node, &ph, header.ph_size, header.ph_pos + currentph*header.ph_size); if(err) return err; if(readc != header.ph_size) return EINVAL; if(ph.type == PH_TYPE_PHDR) progphs = (elf_ph64*)ph.memaddr; if(ph.type == PH_TYPE_INTERPRETER){ // load the name and elf header of the interpreter progheader = header; interp = alloc(ph.fsize + 1); if(!interp) return ENOMEM; readc = vfs_read(&err, node, interp, ph.fsize, ph.offset); if(err){ free(interp); return err; } if(ph.fsize != readc){ free(interp); return EINVAL; } err = vfs_open(&node, thread->proc->root, interp); if(err){ free(interp); return err; } readc = vfs_read(&err, node, &header, sizeof(elf_header64), 0); if(err) goto _fail; if(readc != sizeof(elf_header64) || (!check_header64(&header))){ err = EINVAL; goto _fail; } } } // load program into memory for(size_t currentph = 0; currentph < header.ph_count; ++currentph){ // read ph readc = vfs_read(&err, node, &ph, header.ph_size, header.ph_pos + currentph*header.ph_size); if(err) goto _fail; if(readc != header.ph_size){ err = EINVAL; goto _fail; } if(ph.type != PH_TYPE_LOAD) continue; if(!vmm_allocnowat((void*)ph.memaddr, phflagtommuflag(ph.flags), ph.msize)){ err = ENOMEM; goto _fail; } // set up temporary mmu flags to copy data arch_mmu_changeflags(arch_getcls()->context->context, ph.memaddr, ARCH_MMU_MAP_READ | ARCH_MMU_MAP_WRITE, ph.msize / PAGE_SIZE + (ph.msize % PAGE_SIZE ? 1 : 0)); // read program data readc = vfs_read(&err, node, (void*)ph.memaddr, ph.fsize, ph.offset); if(err) goto _fail; if(readc != ph.fsize){ err = EINVAL; goto _fail; } // zero if needed size_t zerocount = ph.msize - ph.fsize; if(zerocount){ void* zeroaddr = ph.memaddr + ph.fsize; memset(zeroaddr, 0, zerocount); } // reset proper mmu flags arch_mmu_changeflags(arch_getcls()->context->context, ph.memaddr, phflagtommuflag(ph.flags), ph.msize / PAGE_SIZE + (ph.msize % PAGE_SIZE ? 1 : 0)); } // also load the executable memory image if we're using an interpreter if(interp){ for(size_t currentph = 0; currentph < header.ph_count; ++currentph){ // read ph readc = vfs_read(&err, prognode, &ph, progheader.ph_size, progheader.ph_pos + currentph*progheader.ph_size); if(err) goto _fail; if(readc != progheader.ph_size){ err = EINVAL; goto _fail; } if(ph.type != PH_TYPE_LOAD) continue; if(!vmm_allocnowat((void*)ph.memaddr, phflagtommuflag(ph.flags), ph.msize)){ err = ENOMEM; goto _fail; } // set up temporary mmu flags to copy data arch_mmu_changeflags(arch_getcls()->context->context, ph.memaddr, ARCH_MMU_MAP_READ | ARCH_MMU_MAP_WRITE, ph.msize / PAGE_SIZE + (ph.msize % PAGE_SIZE ? 1 : 0)); // read program data readc = vfs_read(&err, prognode, (void*)ph.memaddr, ph.fsize, ph.offset); if(err) goto _fail; if(readc != ph.fsize){ err = EINVAL; goto _fail; } // zero if needed size_t zerocount = ph.msize - ph.fsize; if(zerocount){ void* zeroaddr = ph.memaddr + ph.fsize; memset(zeroaddr, 0, zerocount); } // reset proper mmu flags arch_mmu_changeflags(arch_getcls()->context->context, ph.memaddr, phflagtommuflag(ph.flags), ph.msize / PAGE_SIZE + (ph.msize % PAGE_SIZE ? 1 : 0)); } } void* stack = stacksetup(interp, argv, env, progheader, progphs); arch_regs_setupuser(thread->regs, (void*)header.entry, stack, true); return 0; _fail: if(interp){ free(interp); vfs_close(node); } return err; }