123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331 |
- /* vim: tabstop=4 shiftwidth=4 noexpandtab
- * This file is part of ToaruOS and is released under the terms
- * of the NCSA / University of Illinois License - see LICENSE.md
- * Copyright (C) 2011-2018 K. Lange
- *
- * ELF Static Executable Loader
- *
- */
- #include <kernel/system.h>
- #include <kernel/fs.h>
- #include <kernel/elf.h>
- #include <kernel/process.h>
- #include <kernel/logging.h>
- int exec_elf(char * path, fs_node_t * file, int argc, char ** argv, char ** env, int interp) {
- Elf32_Header header;
- read_fs(file, 0, sizeof(Elf32_Header), (uint8_t *)&header);
- if (header.e_ident[0] != ELFMAG0 ||
- header.e_ident[1] != ELFMAG1 ||
- header.e_ident[2] != ELFMAG2 ||
- header.e_ident[3] != ELFMAG3) {
- debug_print(ERROR, "Not a valid ELF executable.");
- close_fs(file);
- return -1;
- }
- if (file->mask & 0x800) {
- debug_print(WARNING, "setuid binary executed [%s, uid:%d]", file->name, file->uid);
- current_process->user = file->uid;
- }
- for (uintptr_t x = 0; x < (uint32_t)header.e_phentsize * header.e_phnum; x += header.e_phentsize) {
- Elf32_Phdr phdr;
- read_fs(file, header.e_phoff + x, sizeof(Elf32_Phdr), (uint8_t *)&phdr);
- if (phdr.p_type == PT_DYNAMIC) {
- /* Dynamic */
- close_fs(file);
- /* Find interpreter? */
- debug_print(INFO, "Dynamic executable");
- unsigned int nargc = argc + 3;
- char * args[nargc+1];
- args[0] = "ld.so";
- args[1] = "-e";
- args[2] = strdup(current_process->name);
- int j = 3;
- for (int i = 0; i < argc; ++i, ++j) {
- args[j] = argv[i];
- }
- args[j] = NULL;
- fs_node_t * file = kopen("/lib/ld.so",0);
- if (!file) return -1;
- return exec_elf(NULL, file, nargc, args, env, 1);
- }
- }
- uintptr_t entry = (uintptr_t)header.e_entry;
- uintptr_t base_addr = 0xFFFFFFFF;
- uintptr_t end_addr = 0x0;
- for (uintptr_t x = 0; x < (uint32_t)header.e_phentsize * header.e_phnum; x += header.e_phentsize) {
- Elf32_Phdr phdr;
- read_fs(file, header.e_phoff + x, sizeof(Elf32_Phdr), (uint8_t *)&phdr);
- if (phdr.p_type == PT_LOAD) {
- if (phdr.p_vaddr < base_addr) {
- base_addr = phdr.p_vaddr;
- }
- if (phdr.p_memsz + phdr.p_vaddr > end_addr) {
- end_addr = phdr.p_memsz + phdr.p_vaddr;
- }
- }
- }
- current_process->image.entry = base_addr;
- current_process->image.size = end_addr - base_addr;
- release_directory_for_exec(current_directory);
- invalidate_page_tables();
- for (uintptr_t x = 0; x < (uint32_t)header.e_phentsize * header.e_phnum; x += header.e_phentsize) {
- Elf32_Phdr phdr;
- read_fs(file, header.e_phoff + x, sizeof(Elf32_Phdr), (uint8_t *)&phdr);
- if (phdr.p_type == PT_LOAD) {
- /* TODO: These virtual address bounds should be in a header somewhere */
- if (phdr.p_vaddr < 0x20000000) return -EINVAL;
- /* TODO Upper bounds */
- for (uintptr_t i = phdr.p_vaddr; i < phdr.p_vaddr + phdr.p_memsz; i += 0x1000) {
- /* This doesn't care if we already allocated this page */
- alloc_frame(get_page(i, 1, current_directory), 0, 1);
- invalidate_tables_at(i);
- }
- IRQ_RES;
- read_fs(file, phdr.p_offset, phdr.p_filesz, (uint8_t *)phdr.p_vaddr);
- IRQ_OFF;
- size_t r = phdr.p_filesz;
- while (r < phdr.p_memsz) {
- *(char *)(phdr.p_vaddr + r) = 0;
- r++;
- }
- }
- }
- close_fs(file);
- for (uintptr_t stack_pointer = USER_STACK_BOTTOM; stack_pointer < USER_STACK_TOP; stack_pointer += 0x1000) {
- alloc_frame(get_page(stack_pointer, 1, current_directory), 0, 1);
- invalidate_tables_at(stack_pointer);
- }
- /* Collect arguments */
- int envc = 0;
- for (envc = 0; env[envc] != NULL; ++envc);
- /* Format auxv */
- Elf32_auxv auxv[] = {
- {256, 0xDEADBEEF},
- {0, 0}
- };
- int auxvc = 0;
- for (auxvc = 0; auxv[auxvc].id != 0; ++auxvc);
- auxvc++;
- uintptr_t heap = current_process->image.entry + current_process->image.size;
- while (heap & 0xFFF) heap++;
- alloc_frame(get_page(heap, 1, current_directory), 0, 1);
- invalidate_tables_at(heap);
- char ** argv_ = (char **)heap;
- heap += sizeof(char *) * (argc + 1);
- char ** env_ = (char **)heap;
- heap += sizeof(char *) * (envc + 1);
- void * auxv_ptr = (void *)heap;
- heap += sizeof(Elf32_auxv) * (auxvc);
- for (int i = 0; i < argc; ++i) {
- size_t size = strlen(argv[i]) * sizeof(char) + 1;
- for (uintptr_t x = heap; x < heap + size + 0x1000; x += 0x1000) {
- alloc_frame(get_page(x, 1, current_directory), 0, 1);
- }
- invalidate_tables_at(heap);
- argv_[i] = (char *)heap;
- memcpy((void *)heap, argv[i], size);
- heap += size;
- }
- /* Don't forget the NULL at the end of that... */
- argv_[argc] = 0;
- for (int i = 0; i < envc; ++i) {
- size_t size = strlen(env[i]) * sizeof(char) + 1;
- for (uintptr_t x = heap; x < heap + size + 0x1000; x += 0x1000) {
- alloc_frame(get_page(x, 1, current_directory), 0, 1);
- }
- invalidate_tables_at(heap);
- env_[i] = (char *)heap;
- memcpy((void *)heap, env[i], size);
- heap += size;
- }
- env_[envc] = 0;
- memcpy(auxv_ptr, auxv, sizeof(Elf32_auxv) * (auxvc));
- current_process->image.heap = heap; /* heap end */
- current_process->image.heap_actual = heap + (0x1000 - heap % 0x1000);
- alloc_frame(get_page(current_process->image.heap_actual, 1, current_directory), 0, 1);
- invalidate_tables_at(current_process->image.heap_actual);
- current_process->image.user_stack = USER_STACK_TOP;
- current_process->image.start = entry;
- /* Close all fds >= 3 */
- for (unsigned int i = 3; i < current_process->fds->length; ++i) {
- if (current_process->fds->entries[i]) {
- close_fs(current_process->fds->entries[i]);
- current_process->fds->entries[i] = NULL;
- }
- }
- /* Go go go */
- enter_user_jmp(entry, argc, argv_, USER_STACK_TOP);
- /* We should never reach this code */
- return -1;
- }
- int exec_shebang(char * path, fs_node_t * file, int argc, char ** argv, char ** env, int interp) {
- /* Read MAX_LINE... */
- char tmp[100];
- read_fs(file, 0, 100, (unsigned char *)tmp); close_fs(file);
- char * cmd = (char *)&tmp[2];
- if (*cmd == ' ') cmd++; /* Handle a leading space */
- char * space_or_linefeed = strpbrk(cmd, " \n");
- char * arg = NULL;
- if (!space_or_linefeed) {
- debug_print(WARNING, "No space or linefeed found.");
- return -ENOEXEC;
- }
- if (*space_or_linefeed == ' ') {
- /* Oh lovely, an argument */
- *space_or_linefeed = '\0';
- space_or_linefeed++;
- arg = space_or_linefeed;
- space_or_linefeed = strpbrk(space_or_linefeed, "\n");
- if (!space_or_linefeed) {
- debug_print(WARNING, "Argument exceeded maximum length");
- return -ENOEXEC;
- }
- }
- *space_or_linefeed = '\0';
- char script[strlen(path)+1];
- memcpy(script, path, strlen(path)+1);
- unsigned int nargc = argc + (arg ? 2 : 1);
- char * args[nargc + 2];
- args[0] = cmd;
- args[1] = arg ? arg : script;
- args[2] = arg ? script : NULL;
- args[3] = NULL;
- int j = arg ? 3 : 2;
- for (int i = 1; i < argc; ++i, ++j) {
- args[j] = argv[i];
- }
- args[j] = NULL;
- return exec(cmd, nargc, args, env);
- }
- /* Consider exposing this and making it a list so it can be extended ... */
- typedef int (*exec_func)(char * path, fs_node_t * file, int argc, char ** argv, char ** env, int interp);
- typedef struct {
- exec_func func;
- unsigned char bytes[4];
- unsigned int match;
- char * name;
- } exec_def_t;
- exec_def_t fmts[] = {
- {exec_elf, {ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3}, 4, "ELF"},
- {exec_shebang, {'#', '!', 0, 0}, 2, "#!"},
- };
- static int matches(unsigned char * a, unsigned char * b, unsigned int len) {
- for (unsigned int i = 0; i < len; ++i) {
- if (a[i] != b[i]) return 0;
- }
- return 1;
- }
- /**
- * Load an execute a binary.
- *
- * This determines the binary type (eg., ELF binary, she-bang script, etc.)
- * and then calls the appropriate underlying exec function.
- *
- * @param path Path to the executable to attempt to execute.
- * @param argc Number of arguments (because I'm not counting for you)
- * @param argv Pointer to a string of arguments
- */
- int exec(
- char * path, /* Path to the executable to run */
- int argc, /* Argument count (ie, /bin/echo hello world = 3) */
- char ** argv, /* Argument strings (including executable path) */
- char ** env /* Environmen variables */
- ) {
- /* Open the file */
- fs_node_t * file = kopen(path,0);
- if (!file) {
- /* Command not found */
- return -ENOENT;
- }
- if (!has_permission(file, 01)) {
- return -EACCES;
- }
- /* Read four bytes of the file */
- unsigned char head[4];
- read_fs(file, 0, 4, head);
- debug_print(INFO, "First four bytes: %c%c%c%c", head[0], head[1], head[2], head[3]);
- current_process->name = strdup(path);
- gettimeofday((struct timeval *)¤t_process->start, NULL);
- for (unsigned int i = 0; i < sizeof(fmts) / sizeof(exec_def_t); ++i) {
- if (matches(fmts[i].bytes, head, fmts[i].match)) {
- debug_print(NOTICE, "Matched executor: %s", fmts[i].name);
- return fmts[i].func(path, file, argc, argv, env, 0);
- }
- }
- debug_print(WARNING, "Exec failed?");
- return -ENOEXEC;
- }
- int
- system(
- char * path, /* Path to the executable to run */
- int argc, /* Argument count (ie, /bin/echo hello world = 3) */
- char ** argv, /* Argument strings (including executable path) */
- char ** envin
- ) {
- char ** argv_ = malloc(sizeof(char *) * (argc + 1));
- for (int j = 0; j < argc; ++j) {
- argv_[j] = malloc((strlen(argv[j]) + 1) * sizeof(char));
- memcpy(argv_[j], argv[j], strlen(argv[j]) + 1);
- }
- argv_[argc] = 0;
- char * env[] = {NULL};
- set_process_environment((process_t*)current_process, clone_directory(current_directory));
- current_directory = current_process->thread.page_directory;
- switch_page_directory(current_directory);
- current_process->cmdline = argv_;
- exec(path,argc,argv_,envin ? envin : env);
- debug_print(ERROR, "Failed to execute process!");
- kexit(-1);
- return -1;
- }
|