From 7d20c507b16990ff4989566faccf4296ff1f74a5 Mon Sep 17 00:00:00 2001 From: apio Date: Wed, 26 Oct 2022 18:57:06 +0200 Subject: [PATCH] Kernel, libc, userspace: Implement command-line arguments (argv) The only thing missing now is for sh to pass them on. --- apps/Makefile | 2 +- apps/src/args.c | 7 ++ apps/src/init.c | 3 +- apps/src/sh.c | 8 ++- kernel/include/std/errno.h | 1 + kernel/include/sys/Syscall.h | 4 +- kernel/src/sys/Syscall.cpp | 2 +- kernel/src/sys/exec.cpp | 113 +++++++++++++++++++++++++++++++- libs/libc/crt0.asm | 9 +-- libs/libc/include/errno.h | 1 + libs/libc/include/sys/syscall.h | 2 +- libs/libc/include/unistd.h | 5 +- libs/libc/src/init.cpp | 2 - libs/libc/src/string.cpp | 1 + libs/libc/src/syscall.cpp | 2 +- libs/libc/src/unistd.cpp | 4 +- 16 files changed, 144 insertions(+), 22 deletions(-) create mode 100644 apps/src/args.c diff --git a/apps/Makefile b/apps/Makefile index 68f40ffe..66d0e8da 100644 --- a/apps/Makefile +++ b/apps/Makefile @@ -1,4 +1,4 @@ -APPS := init sym sh crash uname uptime hello ps ls +APPS := init sym sh crash uname uptime hello ps ls args APPS_DIR := $(LUNA_ROOT)/apps APPS_SRC := $(APPS_DIR)/src diff --git a/apps/src/args.c b/apps/src/args.c new file mode 100644 index 00000000..aa3bf2f3 --- /dev/null +++ b/apps/src/args.c @@ -0,0 +1,7 @@ +#include +#include + +int main(int argc, char** argv) +{ + for (int i = 0; i < argc; i++) puts(argv[i]); +} \ No newline at end of file diff --git a/apps/src/init.c b/apps/src/init.c index 3746db80..6f1b56c3 100644 --- a/apps/src/init.c +++ b/apps/src/init.c @@ -50,7 +50,8 @@ int main() } if (child == 0) { - execv("/bin/sh", NULL); + char* argv[] = {"/bin/sh", NULL}; + execv(argv[0], argv); perror("execv"); return 1; } diff --git a/apps/src/sh.c b/apps/src/sh.c index fd31d081..dc4f0300 100644 --- a/apps/src/sh.c +++ b/apps/src/sh.c @@ -113,11 +113,15 @@ void command_execute(command* cmd) char* buf = malloc(cmd->size + 6); strlcpy(buf, "/bin/", 6); strncat(buf, cmd->buffer, cmd->size); - execv(buf, NULL); + char* argv[] = {buf, NULL}; + execv(argv[0], argv); } } else - execv(cmd->buffer, NULL); + { + char* argv[] = {cmd->buffer, NULL}; + execv(argv[0], argv); + } perror(cmd->buffer); exit(127); } diff --git a/kernel/include/std/errno.h b/kernel/include/std/errno.h index 2ff1a8f8..19a0db94 100644 --- a/kernel/include/std/errno.h +++ b/kernel/include/std/errno.h @@ -4,6 +4,7 @@ #define ENOENT 2 #define ESRCH 3 #define EINTR 4 +#define E2BIG 7 #define ENOEXEC 8 #define EBADF 9 #define EAGAIN 11 diff --git a/kernel/include/sys/Syscall.h b/kernel/include/sys/Syscall.h index 4428652b..b5dfb2bd 100644 --- a/kernel/include/sys/Syscall.h +++ b/kernel/include/sys/Syscall.h @@ -14,7 +14,7 @@ #define SYS_read 9 #define SYS_close 10 #define SYS_seek 11 -#define SYS_exec 12 +#define SYS_execv 12 #define SYS_fcntl 13 #define SYS_mprotect 14 #define SYS_clock 15 @@ -49,7 +49,7 @@ void sys_open(Context* context, const char* filename, int flags); void sys_read(Context* context, int fd, size_t size, char* buffer); void sys_close(Context* context, int fd); void sys_seek(Context* context, int fd, long offset, int whence); -void sys_exec(Context* context, const char* pathname); +void sys_execv(Context* context, const char* pathname, char** argv); void sys_fcntl(Context* context, int fd, int command, uintptr_t arg); void sys_mprotect(Context* context, void* address, size_t size, int prot); void sys_clock(Context* context); diff --git a/kernel/src/sys/Syscall.cpp b/kernel/src/sys/Syscall.cpp index 925cf0c8..ac1e9a04 100644 --- a/kernel/src/sys/Syscall.cpp +++ b/kernel/src/sys/Syscall.cpp @@ -23,7 +23,7 @@ void Syscall::entry(Context* context) case SYS_read: sys_read(context, (int)context->rdi, context->rsi, (char*)context->rdx); break; case SYS_close: sys_close(context, (int)context->rdi); break; case SYS_seek: sys_seek(context, (int)context->rdi, (long)context->rsi, (int)context->rdx); break; - case SYS_exec: sys_exec(context, (const char*)context->rdi); break; + case SYS_execv: sys_execv(context, (const char*)context->rdi, (char**)context->rsi); break; case SYS_fcntl: sys_fcntl(context, (int)context->rdi, (int)context->rsi, context->rdx); break; case SYS_mprotect: sys_mprotect(context, (void*)context->rdi, context->rsi, (int)context->rdx); break; case SYS_clock: sys_clock(context); break; diff --git a/kernel/src/sys/exec.cpp b/kernel/src/sys/exec.cpp index 0e36861a..2a601e5a 100644 --- a/kernel/src/sys/exec.cpp +++ b/kernel/src/sys/exec.cpp @@ -2,6 +2,7 @@ #include "interrupts/Interrupts.h" #include "log/Log.h" +#include "memory/Memory.h" #include "memory/MemoryManager.h" #include "memory/PMM.h" #include "memory/VMM.h" @@ -56,7 +57,16 @@ void sys_fork(Context* context) return; } -void sys_exec(Context* context, const char* pathname) +void push_on_user_stack(uint64_t* rsp, char* value, + size_t size) // FIXME: Handle segments of stack that extend beyond one page. +{ + (*rsp) -= size; + char* kvalue = (char*)VMM::get_physical(*rsp); + ASSERT(kvalue != (char*)UINT64_MAX); + memcpy(kvalue, value, size); +} + +void sys_execv(Context* context, const char* pathname, char** argv) { char* kpathname = strdup_from_user(pathname); if (!kpathname) @@ -97,6 +107,85 @@ void sys_exec(Context* context, const char* pathname) return; } + uint64_t kargc = 0; + char** kargv = nullptr; + char** arg; + + auto free_kernel_argv_copy = [&]() { + for (uint64_t i = 0; i < kargc; i++) + { + if (kargv[i]) kfree(kargv[i]); + } + if (kargv) kfree(kargv); + }; + + do { + if (Memory::is_kernel_address((uintptr_t)argv)) + { + free_kernel_argv_copy(); + context->rax = -EFAULT; + return; + } + arg = (char**)VMM::get_physical((uint64_t)argv); + if (arg == (char**)UINT64_MAX) + { + free_kernel_argv_copy(); + context->rax = -EFAULT; + return; + } + kargv = (char**)krealloc(kargv, (kargc + 1) * sizeof(char*)); + if (!kargv) + { + free_kernel_argv_copy(); + context->rax = -ENOMEM; + return; + } + if (*arg) + { + char* kcopy = strdup_from_user(*arg); + if (!kcopy) + { + free_kernel_argv_copy(); + context->rax = -ENOMEM; + return; + } + kargv[kargc] = kcopy; + } + else + { + kargv[kargc] = nullptr; + break; + } + kargc++; + argv++; + } while (arg != nullptr); + + kinfoln("Copied %lu arguments from user process", kargc); + + size_t stack_size = 0; + for (uint64_t i = 0; i <= kargc; i++) + { + stack_size += sizeof(char*); + if (kargv[i]) { stack_size += strlen(kargv[i]); } + } + + if (stack_size > + ((TASK_PAGES_IN_STACK / 2) * + PAGE_SIZE)) // FIXME: Maybe we should allocate a larger stack in this case, but still set a larger upper limit. + { + free_kernel_argv_copy(); + context->rax = -E2BIG; + return; + } + + char** user_argv = (char**)kcalloc(kargc + 1, sizeof(char*)); + if (!user_argv) + { + free_kernel_argv_copy(); + context->rax = -ENOMEM; + return; + } + Interrupts::disable(); ASSERT(!Interrupts::are_enabled()); // This part is pretty sensitive. @@ -129,9 +218,29 @@ void sys_exec(Context* context, const char* pathname) if (file.close_on_exec()) { file.close(); } } - task->restore_context(context); + for (uint64_t i = 0; i <= kargc; i++) + { + if (kargv[i]) + { + push_on_user_stack(&task->regs.rsp, kargv[i], strlen(kargv[i]) + 1); + user_argv[i] = (char*)task->regs.rsp; + } + else + user_argv[i] = nullptr; + } + push_on_user_stack(&task->regs.rsp, (char*)user_argv, (kargc + 1) * sizeof(char*)); + task->regs.rdi = kargc; // argc + task->regs.rsi = task->regs.rsp; // argv + + task->regs.rsp &= (UINT64_MAX ^ 15); // align it + + free_kernel_argv_copy(); + + kfree(user_argv); kfree(kpathname); + task->restore_context(context); + return; } \ No newline at end of file diff --git a/libs/libc/crt0.asm b/libs/libc/crt0.asm index 958d08b3..71973e6a 100644 --- a/libs/libc/crt0.asm +++ b/libs/libc/crt0.asm @@ -6,8 +6,6 @@ extern _fini extern initialize_libc extern exit -extern __argv - global _start _start: ; Set up end of the stack frame linked list. @@ -16,12 +14,15 @@ _start: push rbp ; rbp=0 mov rbp, rsp + push rdi + push rsi + call initialize_libc call _init - mov rdi, 0 ; argc = 0 - mov rsi, __argv ; Dummy argv which is equal to {NULL} + pop rsi ; argv + pop rdi ; argc call main diff --git a/libs/libc/include/errno.h b/libs/libc/include/errno.h index d69edf81..dca6d19e 100644 --- a/libs/libc/include/errno.h +++ b/libs/libc/include/errno.h @@ -8,6 +8,7 @@ extern int errno; #define ENOENT 2 // No such file or directory #define ESRCH 3 // No such process #define EINTR 4 // Interrupted system call. Not implemented. +#define E2BIG 7 // Argument list too long #define ENOEXEC 8 // Exec format error #define EBADF 9 // Bad file descriptor #define EAGAIN 11 // Resource temporarily unavailable diff --git a/libs/libc/include/sys/syscall.h b/libs/libc/include/sys/syscall.h index 00939aa0..5b02e983 100644 --- a/libs/libc/include/sys/syscall.h +++ b/libs/libc/include/sys/syscall.h @@ -13,7 +13,7 @@ #define SYS_read 9 #define SYS_close 10 #define SYS_seek 11 -#define SYS_exec 12 +#define SYS_execv 12 #define SYS_fcntl 13 #define SYS_mprotect 14 #define SYS_clock 15 diff --git a/libs/libc/include/unistd.h b/libs/libc/include/unistd.h index d6e57abf..ba410e8e 100644 --- a/libs/libc/include/unistd.h +++ b/libs/libc/include/unistd.h @@ -20,9 +20,8 @@ extern "C" { #endif - /* Executes the program program. On success, does not return. The second parameter (arguments) is not implemented - * for now. You can pass NULL to it. */ - int execv(const char* program, char* const[]); + /* Executes the program program, passing the arguments in the argument list argv. On success, does not return. */ + int execv(const char* program, char* const argv[]); /* Not implemented. */ int execve(const char*, char* const[], char* const[]); diff --git a/libs/libc/src/init.cpp b/libs/libc/src/init.cpp index 07caefc2..330ca048 100644 --- a/libs/libc/src/init.cpp +++ b/libs/libc/src/init.cpp @@ -4,8 +4,6 @@ #include #include -__lc_used const char* __argv[] = {NULL}; // For now. - static void terminate_libc() { fclose(stdout); diff --git a/libs/libc/src/string.cpp b/libs/libc/src/string.cpp index a9a4b4ed..3e896f85 100644 --- a/libs/libc/src/string.cpp +++ b/libs/libc/src/string.cpp @@ -261,6 +261,7 @@ extern "C" case ENOENT: return "No such file or directory"; case ESRCH: return "No such process"; case EINTR: return "Interrupted system call"; + case E2BIG: return "Argument list too long"; case ENOEXEC: return "Exec format error"; case EBADF: return "Bad file descriptor"; case EAGAIN: return "Resource temporarily unavailable"; diff --git a/libs/libc/src/syscall.cpp b/libs/libc/src/syscall.cpp index bd3e1fbe..9b655704 100644 --- a/libs/libc/src/syscall.cpp +++ b/libs/libc/src/syscall.cpp @@ -18,10 +18,10 @@ extern "C" long syscall(long number, ...) case SYS_exit: case SYS_getprocid: case SYS_close: - case SYS_exec: case SYS_mkdir: case SYS_sleep: result = __luna_syscall1(number, va_arg(ap, arg)); break; case SYS_munmap: + case SYS_execv: case SYS_access: case SYS_fstat: case SYS_stat: diff --git a/libs/libc/src/unistd.cpp b/libs/libc/src/unistd.cpp index a0955254..73967432 100644 --- a/libs/libc/src/unistd.cpp +++ b/libs/libc/src/unistd.cpp @@ -8,9 +8,9 @@ extern "C" { - int execv(const char* program, char* const[]) + int execv(const char* program, char* const argv[]) { - return (int)syscall(SYS_exec, program); + return (int)syscall(SYS_execv, program, argv); } int execve(const char*, char* const[], char* const[])