int arch_loader_parse_elf_executable(void *mem, struct file *file, addr_t *start, addr_t *end) { uint32_t i, x; addr_t entry; elf_header_t *eh = (elf_header_t *)mem; uint8_t buffer[(eh->phnum+1)*eh->phsize]; fs_file_pread(file, eh->phoff, buffer, eh->phsize * eh->phnum); uint64_t vaddr=0, length=0, offset=0, stop, tmp; uint64_t max=0, min=~0; for(i=0;i < eh->phnum;i++) { elf64_program_header_t *ph = (elf64_program_header_t *)(buffer + (i*eh->phsize)); if((ph->p_addr + ph->p_memsz) > max) max = ph->p_addr + ph->p_memsz; if(ph->p_addr < min) min = ph->p_addr; if(ph->p_type == PH_LOAD) { /* mmap program headers. if the memsz is the same as the filesz, we don't have * to do anything special. if not, then we might need additional mappings: the * file is mapped to some section of the program header's region, and then the * rest is MAP_ANONYMOUS memory. if it fits in the end of the page for the file * mapped memory, then it can fit there. otherwise, we call mmap again. * * also, the actual section we want might be offset from a page. handle that as * well, with inpage_offset. */ size_t additional = ph->p_memsz - ph->p_filesz; size_t inpage_offset = ph->p_addr & (~PAGE_MASK); addr_t newend = (ph->p_addr + ph->p_filesz); size_t page_free = PAGE_SIZE - (newend % PAGE_SIZE); int prot = 0; if(ph->p_flags & ELF_PF_R) prot |= PROT_READ; if(ph->p_flags & ELF_PF_W) prot |= PROT_WRITE; if(ph->p_flags & ELF_PF_X) prot |= PROT_EXEC; int flags = MAP_FIXED; if(prot & PROT_WRITE) flags |= MAP_PRIVATE; else flags |= MAP_SHARED; mm_mmap(ph->p_addr & PAGE_MASK, ph->p_filesz + inpage_offset, prot, flags, file, ph->p_offset & PAGE_MASK, 0); if(additional > page_free) { mm_mmap((newend & PAGE_MASK) + PAGE_SIZE, additional - page_free, prot, flags | MAP_ANONYMOUS, 0, 0, 0); } } } if(!max) return 0; *start = eh->entry; *end = (max & PAGE_MASK) + PAGE_SIZE; return 1; }
int ext2_read_off(struct ext2_info *fs, off_t off, unsigned char *buf, size_t len) { struct file f; f.inode = fs->filesys->node; int ret = fs_file_pread(&f, off, buf, len); return ret; }
ssize_t fs_file_read(struct file *file, uint8_t *buffer, size_t length) { ssize_t ret = fs_file_pread(file, file->pos, buffer, length); if(ret > 0) file->pos += ret; return ret; }
int ext2_read_block(struct ext2_info *fs, uint64_t block, unsigned char *buf) { off_t off = block * ext2_sb_blocksize(fs->sb);// + fs->block*512; struct file f; f.inode = fs->filesys->node; int ret = fs_file_pread(&f, off, buf, ext2_sb_blocksize(fs->sb)); return ret; }
int sys_read(int fp, off_t pos, unsigned char *buf, size_t count) { if(!buf) return -EINVAL; struct file *f = file_get(fp); if(!f) return -EBADF; if(!(f->flags & _FREAD)) { file_put(f); return -EACCES; } int ret = fs_file_pread(f, pos, buf, count); file_put(f); return ret; }
int do_exec(char *path, char **argv, char **env, int shebanged /* oh my */) { unsigned int i=0; addr_t end, eip; unsigned int argc=0, envc=0; char **backup_argv=0, **backup_env=0; /* Sanity */ if(!path || !*path) return -EINVAL; /* Load the file, and make sure that it is valid and accessible */ if(EXEC_LOG == 2) printk(0, "[%d]: Checking executable file (%s)\n", current_process->pid, path); struct file *efil; int err_open; efil = fs_file_open(path, _FREAD, 0, &err_open); if(!efil) return err_open; /* are we allowed to execute it? */ if(!vfs_inode_check_permissions(efil->inode, MAY_EXEC, 0)) { file_put(efil); return -EACCES; } /* is it a valid elf? */ int header_size = 0; #if CONFIG_ARCH == TYPE_ARCH_X86_64 header_size = sizeof(elf64_header_t); #elif CONFIG_ARCH == TYPE_ARCH_X86 header_size = sizeof(elf32_header_t); #endif /* read in the ELF header, and check if it's a shebang */ if(header_size < 2) header_size = 2; unsigned char mem[header_size]; fs_file_pread(efil, 0, mem, header_size); if(__is_shebang(mem)) return loader_do_shebang(efil, argv, env); int other_bitsize=0; if(!is_valid_elf(mem, 2) && !other_bitsize) { file_put(efil); return -ENOEXEC; } if(EXEC_LOG == 2) printk(0, "[%d]: Copy data\n", current_process->pid); /* okay, lets back up argv and env so that we can * clear out the address space and not lose data... * If this call if coming from a shebang, then we don't check the pointers, * since they won't be from userspace */ size_t total_args_len = 0; if((shebanged || mm_is_valid_user_pointer(SYS_EXECVE, argv, 0)) && argv) { while((shebanged || mm_is_valid_user_pointer(SYS_EXECVE, argv[argc], 0)) && argv[argc] && *argv[argc]) argc++; backup_argv = (char **)kmalloc(sizeof(addr_t) * argc); for(i=0;i<argc;i++) { backup_argv[i] = (char *)kmalloc(strlen(argv[i]) + 1); _strcpy(backup_argv[i], argv[i]); total_args_len += strlen(argv[i])+1 + sizeof(char *); } } if((shebanged || mm_is_valid_user_pointer(SYS_EXECVE, env, 0)) && env) { while((shebanged || mm_is_valid_user_pointer(SYS_EXECVE, env[envc], 0)) && env[envc] && *env[envc]) envc++; backup_env = (char **)kmalloc(sizeof(addr_t) * envc); for(i=0;i<envc;i++) { backup_env[i] = (char *)kmalloc(strlen(env[i]) + 1); _strcpy(backup_env[i], env[i]); total_args_len += strlen(env[i])+1 + sizeof(char *); } } total_args_len += 2 * sizeof(char *); /* and the path too! */ char *path_backup = (char *)kmalloc(strlen(path) + 1); _strcpy((char *)path_backup, path); path = path_backup; /* Preexec - This is the point of no return. Here we close out unneeded * file descs, free up the page directory and clear up the resources * of the task */ if(EXEC_LOG) printk(0, "Executing (p%dt%d, cpu %d, tty %d): %s\n", current_process->pid, current_thread->tid, current_thread->cpu->knum, current_process->pty ? current_process->pty->num : 0, path); preexec(); /* load in the new image */ strncpy((char *)current_process->command, path, 128); if(!loader_parse_elf_executable(mem, efil, &eip, &end)) eip=0; /* do setuid and setgid */ if(efil->inode->mode & S_ISUID) { current_process->effective_uid = efil->inode->uid; } if(efil->inode->mode & S_ISGID) { current_process->effective_gid = efil->inode->gid; } /* we don't need the file anymore, close it out */ file_put(efil); file_close_cloexec(); if(!eip) { printk(5, "[exec]: Tried to execute an invalid ELF file!\n"); free_dp(backup_argv, argc); free_dp(backup_env, envc); kfree(path); tm_thread_exit(0); } if(EXEC_LOG == 2) printk(0, "[%d]: Updating task values\n", current_process->pid); /* Setup the task with the proper values (libc malloc stack) */ addr_t end_l = end; end = ((end-1)&PAGE_MASK) + PAGE_SIZE; total_args_len += PAGE_SIZE; /* now we need to copy back the args and env into userspace * writeable memory...yippie. */ addr_t args_start = end + PAGE_SIZE; addr_t env_start = args_start; addr_t alen = 0; mm_mmap(end, total_args_len, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0, 0); if(backup_argv) { memcpy((void *)args_start, backup_argv, sizeof(addr_t) * argc); alen += sizeof(addr_t) * argc; *(addr_t *)(args_start + alen) = 0; /* set last argument value to zero */ alen += sizeof(addr_t); argv = (char **)args_start; for(i=0;i<argc;i++) { char *old = argv[i]; char *new = (char *)(args_start+alen); unsigned len = strlen(old) + 4; argv[i] = new; _strcpy(new, old); kfree(old); alen += len; } kfree(backup_argv); } env_start = args_start + alen; alen = 0; if(backup_env) { memcpy((void *)env_start, backup_env, sizeof(addr_t) * envc); alen += sizeof(addr_t) * envc; *(addr_t *)(env_start + alen) = 0; /* set last argument value to zero */ alen += sizeof(addr_t); env = (char **)env_start; for(i=0;i<envc;i++) { char *old = env[i]; char *new = (char *)(env_start+alen); unsigned len = strlen(old) + 1; env[i] = new; _strcpy(new, old); kfree(old); alen += len; } kfree(backup_env); } end = (env_start + alen) & PAGE_MASK; current_process->env = env; current_process->argv = argv; kfree(path); /* set the heap locations, and map in the start */ current_process->heap_start = current_process->heap_end = end + PAGE_SIZE*2; addr_t ret = mm_mmap(end + PAGE_SIZE, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0, 0); /* now, we just need to deal with the syscall return stuff. When the syscall * returns, it'll just jump into the entry point of the new process */ tm_thread_lower_flag(current_thread, THREAD_SCHEDULE); /* the kernel cares if it has executed something or not */ if(!(kernel_state_flags & KSF_HAVEEXECED)) set_ksf(KSF_HAVEEXECED); arch_loader_exec_initializer(argc, eip); if(EXEC_LOG == 2) printk(0, "[%d]: Performing call\n", current_process->pid); return 0; }