Start on dynamic loader

This commit is contained in:
flysand7 2023-08-02 14:43:07 +11:00
parent 8751a697ee
commit c293fc7813
5 changed files with 317 additions and 1 deletions

14
b Executable file
View File

@ -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

View File

@ -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')

View File

@ -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

84
src/loader/loader-entry.c Normal file
View File

@ -0,0 +1,84 @@
#include <cia-def.h>
#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);
}

197
src/loader/syscall.c Normal file
View File

@ -0,0 +1,197 @@
#include <asm/unistd.h>
#include <asm/signal.h>
#include <asm/ioctls.h>
#include <asm/mman.h>
#include <linux/fs.h>
#include <linux/loop.h>
#include <linux/time.h>
#include <errno.h>
#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);
}