diff options
author | Ian Moffett <ian@osmora.org> | 2024-04-26 22:38:35 -0400 |
---|---|---|
committer | Ian Moffett <ian@osmora.org> | 2024-04-26 22:38:35 -0400 |
commit | 1a6fdb46a9b146573ce517bbf63b4c0e0fb35357 (patch) | |
tree | 813166bcd7d160dee5fb0538f5567694adf03d32 /sys/kern/kern_exec.c | |
parent | 3b618741d5088062cc47f6b9894c039ac559a175 (diff) |
kernel: Add support for execv()
Signed-off-by: Ian Moffett <ian@osmora.org>
Diffstat (limited to 'sys/kern/kern_exec.c')
-rw-r--r-- | sys/kern/kern_exec.c | 234 |
1 files changed, 234 insertions, 0 deletions
diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c new file mode 100644 index 0000000..b5b4cd2 --- /dev/null +++ b/sys/kern/kern_exec.c @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2023-2024 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/sched.h> +#include <sys/system.h> +#include <sys/errno.h> +#include <sys/vfs.h> +#include <sys/exec.h> +#include <sys/filedesc.h> +#include <sys/signal.h> +#include <sys/loader.h> +#include <sys/cdefs.h> +#include <vm/dynalloc.h> +#include <sys/syslog.h> +#include <string.h> + +__MODULE_NAME("exec"); +__KERNEL_META("$Hyra$: kern_exec.c, Ian Marco Moffett, " + "exec() implementation"); + +#define ARG_MAX 1024 + +/* + * Allocates a buffer in in `res' and fetches + * the args from `argv' + * + * @argv: User argv + * @res: Exec args result. + * + * XXX: res->argp is dynamically allocated, remember + * to free it! + */ +static int +exec_get_args(char **argv, struct exec_args *res) +{ + const size_t ARG_LEN = sizeof(char) * ARG_MAX; + char *argp = NULL; + + struct proc *td; + size_t argp_len = 0; + + if (res == NULL) + return -EINVAL; + + /* Allocate argp */ + res->argp = dynalloc(ARG_LEN); + if (res->argp == NULL) + return -ENOMEM; + + td = this_td(); + res->vas = td->addrsp; + + /* Read argv */ + copyin((uintptr_t)argv, &argp, sizeof(char *)); + for (;;) { + if (argp == NULL) { + res->argp[argp_len] = NULL; + break; + } + + /* Fetch this arg and get next argp */ + copyinstr((uintptr_t)argp, res->argp[argp_len++], ARG_MAX); + copyin((uintptr_t)++argv, &argp, sizeof(char *)); + + /* Try to resize the argp buffer */ + res->argp = dynrealloc(res->argp, ARG_LEN * (argp_len + 1)); + if (res->argp == NULL) { + dynfree(res->argp); + return -ENOMEM; + } + } + + return 0; +} + +/* + * Reset the stack of the process. + * + * @td: Target thread. + * @args: Exec args. + * + * Returns the new stack pointer. + */ +static uintptr_t +exec_set_stack(struct proc *td, struct exec_args args) +{ + struct vm_range *stack_range; + uintptr_t stack_top, sp; + + stack_range = &td->addr_range[ADDR_RANGE_STACK]; + stack_top = stack_range->start + (PROC_STACK_SIZE); + + sp = sched_init_stack((void *)stack_top, args); + return sp; +} + +/* + * execv() implementation. + * + * @pathname: Path of file to execute. + * @argv: Args. + * @sp_res: Pointer to new stack pointer + */ +static int +execv(char *pathname, char **argv, uintptr_t *sp_res) +{ + char *bin; + struct filedesc *filedes; + struct vm_range *exec_range; + struct exec_args args; + + struct proc *td = this_td(); + int fd, ret = 0; + int status; + size_t bin_size; + + if ((status = exec_get_args(argv, &args)) != 0) + return status; + + spinlock_acquire(&td->lock); + fd = open(pathname, O_RDONLY); + + if (fd < 0) { + ret = -ENOENT; + goto done; + } + + filedes = fd_from_fdnum(td, fd); + if (__unlikely(filedes == NULL)) { + /* + * Should not happen. The kernel might be in some + * erroneous state. + */ + ret = -EIO; + goto done; + } + + lseek(fd, 0, SEEK_END); + bin_size = filedes->offset; + lseek(fd, 0, SEEK_SET); + + /* Allocate memory for the binary */ + bin = dynalloc(bin_size); + if (bin == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Read-in the binary */ + if ((status = read(fd, bin, bin_size)) < 0) { + ret = status; + goto done; + } + + /* + * Unload the current process image. After we do this, + * we cannot return in this state until we replace it. + * + * XXX: This is one of the last things we do in case of + * errors. + */ + exec_range = &td->addr_range[ADDR_RANGE_EXEC]; + loader_unload(td->addrsp, exec_range); + + /* + * Now we try to load the new program and hope everything + * works... If something goes wrong here then we'll be forced + * to send a SIGSEGV to the thread. + */ + status = loader_load(td->addrsp, bin, &args.auxv, 0, NULL, exec_range); + if (status != 0) { + /* Well shit */ + KERR("Failed to load new process image\n"); + signal_raise(td, SIGSEGV); + for (;;); + } + + *sp_res = exec_set_stack(td, args); + set_frame_ip(td->tf, args.auxv.at_entry); +done: + /* We are done, free argp and release the thread */ + spinlock_release(&td->lock); + dynfree(args.argp); + return ret; +} + +/* + * Arg0: Pathname + * Arg1: Argv + */ +uint64_t +sys_execv(struct syscall_args *args) +{ + uintptr_t sp; + char pathname[PATH_MAX]; + char **argv = (char **)args->arg1; + + int status; + struct proc *td = this_td(); + + copyinstr(args->arg0, pathname, PATH_MAX); + if ((status = execv(pathname, argv, &sp)) != 0) + return status; + + args->ip = get_frame_ip(td->tf); + args->sp = sp; + return 0; +} |