From c293fc7813c57b3323e3f7eb3b7fa206a06126f6 Mon Sep 17 00:00:00 2001 From: flysand7 Date: Wed, 2 Aug 2023 14:43:07 +1100 Subject: [PATCH] Start on dynamic loader --- b | 14 +++ build.py | 10 +- src/loader/loader-entry.asm | 13 +++ src/loader/loader-entry.c | 84 +++++++++++++++ src/loader/syscall.c | 197 ++++++++++++++++++++++++++++++++++++ 5 files changed, 317 insertions(+), 1 deletion(-) create mode 100755 b create mode 100644 src/loader/loader-entry.asm create mode 100644 src/loader/loader-entry.c create mode 100644 src/loader/syscall.c diff --git a/b b/b new file mode 100755 index 0000000..f097f07 --- /dev/null +++ b/b @@ -0,0 +1,14 @@ +#!/bin/bash + +nasm \ + -f elf64 \ + -o bin/loader-entry.o \ + src/loader/loader-entry.asm + +clang -static -shared \ + -nostdlib \ + -ffreestanding \ + -I src/include \ + -o lib/ld.so \ + -Wl,-e,_dlstart \ + bin/loader-entry.o src/loader/loader.c diff --git a/build.py b/build.py index 13531f5..e86c515 100755 --- a/build.py +++ b/build.py @@ -75,6 +75,7 @@ cc_defines = [] cc_flags = ['-nostdlib'] crt_file = 'crt.lib' lib_file = 'cia.lib' +dl_file = 'ld-cia.so' if args.mode == 'release': cc_flags.append('-O2') cc_defines.append('NDEBUG') @@ -263,6 +264,13 @@ if not os.path.exists('bin'): cia_lib = f'lib/{lib_file}' crt_lib = f'lib/{crt_file}' +dl_lib = f'lib/{dl_file}' + +if target == 'linux': + print_step(f'Compiling {dl_lib}\n') + assemble('src/loader/loader-entry.asm', 'bin/loader-entry.o') + compile(['bin/loader-entry.o', 'src/loader/loader-entry.c'], dl_lib, + '-shared -nostdlib -Wl,-e,_dlstart -Wl,--sort-section,alignment -Wl,--sort-common -Wl,--gc-sections -Wl,--hash-style=both -Wl,--no-undefined -Wl,--exclude-libs=ALL -fno-stack-protector') print_step(f'Compiling {crt_file}\n') if target == 'linux': @@ -280,6 +288,6 @@ archive(['bin/ciabatta.o'], cia_lib) if args.test: if target == 'linux': - compile([args.test, crt_lib, cia_lib], 'a', '-pie') + compile([args.test, crt_lib, cia_lib], 'a', f'-pie -Wl,-dynamic-linker,{dl_lib}') elif target == 'windows': compile([args.test, crt_lib, cia_lib], 'a.exe', '-lkernel32.lib') diff --git a/src/loader/loader-entry.asm b/src/loader/loader-entry.asm new file mode 100644 index 0000000..de8e3e0 --- /dev/null +++ b/src/loader/loader-entry.asm @@ -0,0 +1,13 @@ + +bits 64 + +section .text +default rel +global _dlstart +extern _dlstart_c + +_dlstart: + xor rbp, rbp + mov rdi, rsp + lea rsi, [_dlstart_c wrt ..plt] + and rsp, -15 diff --git a/src/loader/loader-entry.c b/src/loader/loader-entry.c new file mode 100644 index 0000000..2298340 --- /dev/null +++ b/src/loader/loader-entry.c @@ -0,0 +1,84 @@ + +#include +#include "syscall.c" + +// void *memset(void *dst, int value, u64 num) { +// u8 *bytes = dst; +// for(int i = 0; i != num; ++i) { +// bytes[i] = value; +// } +// return dst; +// } + +static void write_char(char c) { + _syscall_write(STDOUT_FILENO, &c, 1); +} + +static void write_string(char *str) { + int str_len = 0; + while(str[str_len] != 0) { + str_len += 1; + } + _syscall_write(STDOUT_FILENO, str, str_len); +} + +static void write_int(i64 number) { + if(number < 0) { + write_char('-'); + number = -number; + } + char buf[20]; + buf[19] = 0; + char *p = buf + sizeof buf - 1; + do { + *--p = (number%10) + '0'; + number /= 10; + } while(number > 0); + write_string(p); +} + +static void write_uint(u64 number) { + char buf[20]; + buf[19] = 0; + char *p = buf + sizeof buf - 1; + do { + *--p = (number%10) + '0'; + number /= 10; + } while(number > 0); + write_string(p); +} + +static void write_hex(u64 number) { + write_string("0x"); + char digits[] = "0123456789abcdef"; + char buf[20]; + buf[19] = 0; + char *p = buf + sizeof buf - 1; + for(int i = 0; i < 64; i += 4) { + if(i != 0 && i % 16 == 0) { + *--p = '\''; + } + u8 bits = (number >> i) & 0x0f; + char digit = digits[bits]; + *--p = digit; + } + write_string(p); +} + +void _dlstart_c(u64 *sp, void *dynv) { + write_string("Entered dynamic loader\n"); + int argc = *sp; + char **argv = (void *)(sp+1); + write_string("We're gonna load an executable: "); + write_string(argv[0]); + write_string("\n"); + write_string("With parameters: "); + for(int i = 1; i < argc; ++i) { + if(i != 1) { + write_string(", "); + } + write_string(argv[i]); + } + write_string("\n"); + _syscall_exit(0); +} diff --git a/src/loader/syscall.c b/src/loader/syscall.c new file mode 100644 index 0000000..89923ae --- /dev/null +++ b/src/loader/syscall.c @@ -0,0 +1,197 @@ + +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(STDIN_FILENO) + #define STDIN_FILENO 0 + #define STDOUT_FILENO 1 + #define STDERR_FILENO 2 +#endif + +#if !defined(MAP_SHARED) + #define MAP_SHARED 0x01 + #define MAP_PRIVATE 0x02 + #define MAP_SHARED_VALIDATE 0x03 +#endif + +#if !defined(MAP_FAILED) + #define MAP_FAILED ((void *)-1) +#endif + +// NOTE(bumbread): These are architecture-specific +#if !defined(O_RDONLY) + #define O_RDONLY 0 + #define O_WRONLY 1 + #define O_RDWR 2 + #define O_CREAT 0x40 + #define O_EXCL 0x80 + #define O_NOCTTY 0x100 + #define O_TRUNC 0x200 + #define O_APPEND 0x400 + #define O_NONBLOCK 0x800 + #define O_DIRECTORY 0x10000 +#endif + +#define _SYSCALL_read 0 +#define _SYSCALL_write 1 +#define _SYSCALL_open 2 +#define _SYSCALL_close 3 +#define _SYSCALL_stat 4 +#define _SYSCALL_fstat 5 +#define _SYSCALL_lstat 6 +#define _SYSCALL_poll 7 +#define _SYSCALL_lseek 8 +#define _SYSCALL_mmap 9 +#define _SYSCALL_mprotect 10 +#define _SYSCALL_munmap 11 +#define _SYSCALL_brk 12 +#define _SYSCALL_rt_sigaction 13 +#define _SYSCALL_rt_sigprocmask 14 +#define _SYSCALL_rt_sigreturn 15 +#define _SYSCALL_ioctl 16 +#define _SYSCALL_pread64 17 +#define _SYSCALL_pwrite64 18 +#define _SYSCALL_readv 19 +#define _SYSCALL_writev 20 +#define _SYSCALL_access 21 +#define _SYSCALL_pipe 22 +#define _SYSCALL_select 23 +#define _SYSCALL_sched_yield 24 +#define _SYSCALL_mremap 25 +#define _SYSCALL_msync 26 +#define _SYSCALL_mincore 27 +#define _SYSCALL_madvise 28 +#define _SYSCALL_shmget 29 +#define _SYSCALL_shmat 30 +#define _SYSCALL_shmctl 31 +#define _SYSCALL_dup 32 +#define _SYSCALL_dup2 33 +#define _SYSCALL_pause 34 +#define _SYSCALL_nanosleep 35 +#define _SYSCALL_getitimer 36 +#define _SYSCALL_alarm 37 +#define _SYSCALL_setitimer 38 +#define _SYSCALL_getpid 39 +#define _SYSCALL_sendfile 40 +#define _SYSCALL_socket 41 +#define _SYSCALL_connect 42 +#define _SYSCALL_accept 43 +#define _SYSCALL_sendto 44 +#define _SYSCALL_recvfrom 45 +#define _SYSCALL_sendmsg 46 +#define _SYSCALL_recvmsg 47 +#define _SYSCALL_shutdown 48 +#define _SYSCALL_bind 49 +#define _SYSCALL_listen 50 +#define _SYSCALL_getsockname 51 +#define _SYSCALL_getpeername 52 +#define _SYSCALL_socketpair 53 +#define _SYSCALL_setsockopt 54 +#define _SYSCALL_getsockopt 55 +#define _SYSCALL_clone 56 +#define _SYSCALL_fork 57 +#define _SYSCALL_vfork 58 +#define _SYSCALL_execve 59 +#define _SYSCALL_exit 60 + +#define _SYSCALL_arch_prctl 158 + +// Syscall stubs + +static inline i64 _syscall0(i64 n) { + i64 ret; + asm volatile("syscall" : "=a"(ret) : "a"(n) : "rcx", "r11", "memory"); + return ret; +} + +static inline i64 _syscall1(i64 n, i64 a1) { + i64 ret; + asm volatile("syscall" : "=a"(ret) : "a"(n), "D"(a1) : "rcx", "r11", "memory"); + return ret; +} + +static inline i64 _syscall2(i64 n, i64 a1, i64 a2) { + i64 ret; + asm volatile("syscall" : "=a"(ret) : "a"(n), "D"(a1), "S"(a2) + : "rcx", "r11", "memory"); + return ret; +} + +static inline i64 _syscall3(i64 n, i64 a1, i64 a2, i64 a3) { + i64 ret; + asm volatile("syscall" : "=a"(ret) : "a"(n), "D"(a1), "S"(a2), + "d"(a3) : "rcx", "r11", "memory"); + return ret; +} + +static inline i64 _syscall4(i64 n, i64 a1, i64 a2, i64 a3, i64 a4) { + i64 ret; + register i64 r10 asm("r10") = a4; + asm volatile("syscall" : "=a"(ret) : "a"(n), "D"(a1), "S"(a2), + "d"(a3), "r"(r10): "rcx", "r11", "memory"); + return ret; +} + +static inline i64 _syscall5(i64 n, i64 a1, i64 a2, i64 a3, i64 a4, i64 a5) { + i64 ret; + register i64 r10 asm("r10") = a4; + register i64 r8 asm("r8") = a5; + asm volatile("syscall" : "=a"(ret) : "a"(n), "D"(a1), "S"(a2), + "d"(a3), "r"(r10), "r"(r8) : "rcx", "r11", "memory"); + return ret; +} + +static inline i64 _syscall6(i64 n, i64 a1, i64 a2, i64 a3, i64 a4, i64 a5, i64 a6) { + i64 ret; + register i64 r10 asm("r10") = a4; + register i64 r8 asm("r8") = a5; + register i64 r9 asm("r9") = a6; + asm volatile("syscall" : "=a"(ret) : "a"(n), "D"(a1), "S"(a2), + "d"(a3), "r"(r10), "r"(r8), "r"(r9) : "rcx", "r11", "memory"); + return ret; +} + +// Syscall wrappers + +static inline i64 _syscall_read(u32 fd, char *buf, u64 count) { + return _syscall3(_SYSCALL_read, (i64)fd, (i64)buf, (i64)count); +} + +static inline i64 _syscall_write(u32 fd, char const *buf, u64 count) { + return _syscall3(_SYSCALL_write, (i64)fd, (i64)buf, (u64)count); +} + +static inline i64 _syscall_open(char const *filename, int flags, int mode) { + return _syscall3(_SYSCALL_open, (i64)filename, (i64)flags, (i64)mode); +} + +static inline i64 _syscall_close(u32 fd) { + return _syscall1(_SYSCALL_close, fd); +} + +static inline void *_syscall_mmap(u64 addr, u64 len, u64 prot, u64 flags, u64 fd, u64 offset) { + return (void *)_syscall6(_SYSCALL_mmap, addr, len, prot, flags, fd, offset); +} + +static inline i64 _syscall_munmap(void *addr, u64 len) { + return _syscall2(_SYSCALL_munmap, (u64)addr, len); +} + +_Noreturn static inline void _syscall_exit(int code) { + _syscall1(_SYSCALL_exit, (i64)code); + __builtin_unreachable(); +} + +static inline i64 _syscall_arch_prctl_set(int code, u64 value) { + return _syscall2(_SYSCALL_arch_prctl, code, (i64)value); +} + +static inline i64 _syscall_arch_prctl_get(int code, u64 *value) { + return _syscall2(_SYSCALL_arch_prctl, code, (i64)value); +}