aboutsummaryrefslogtreecommitdiff
path: root/lib/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/src')
-rw-r--r--lib/src/alloc.c87
-rw-r--r--lib/src/common.c38
-rw-r--r--lib/src/fmt.c12
-rw-r--r--lib/src/io.c11
-rw-r--r--lib/src/syscalls.c62
5 files changed, 200 insertions, 10 deletions
diff --git a/lib/src/alloc.c b/lib/src/alloc.c
new file mode 100644
index 0000000..8b05d9e
--- /dev/null
+++ b/lib/src/alloc.c
@@ -0,0 +1,87 @@
+// Copyright (c) 2021 Johannes Stoelp
+
+#include <alloc.h>
+#include <common.h>
+
+#include <stdint.h>
+
+// Extremely simple and non-thread safe implementation of a dynamic
+// memory allocator. Which will greatly suffer under fragmentation as
+// we neither use splitting nor coalesce free blocks. It uses
+// first-fit and always traverses the block list from the beginning.
+//
+// Bottom line, this allocator can be optimized in so many ways but it
+// doesn't really matter for the purpose of this studies and therefore
+// the allocator is implemented in the most naive way.
+
+// Allocation block descriptor.
+struct BlockDescriptor {
+ unsigned mFree;
+ unsigned mSize;
+ struct BlockDescriptor* mNext;
+};
+
+// Global Allocator.
+
+// Size of available memory to the allocator.
+enum { MEMORY_SIZE = 1 * 1024 * 1024 };
+// Memory for the allocator (statically reserved in the `.bss` section).
+uint8_t gMemory[MEMORY_SIZE];
+
+// Top index into `gMemory` to indicate next free memory.
+unsigned gMemoryTop;
+
+// List of allocated blocks (free + used).
+struct BlockDescriptor* gHead;
+
+// Request free memory from `gMemory` and advance the `gMemoryTop` index.
+static void* brk(unsigned size) {
+ ERROR_ON(gMemoryTop + size >= MEMORY_SIZE, "Allocator OOM!");
+ const unsigned old_top = gMemoryTop;
+ gMemoryTop += size;
+ return (void*)(gMemory + old_top);
+}
+
+// Allocate memory chunk of `size` and return pointer to the chunk.
+void* alloc(unsigned size) {
+ struct BlockDescriptor* current = 0;
+
+ // Check if we have a free block in the list of allocated blocks
+ // that matches the requested size.
+ current = gHead;
+ while (current) {
+ if (current->mFree && current->mSize < size) {
+ current->mFree = 0;
+ return (void*)(current + 1);
+ };
+ current = current->mNext;
+ }
+
+ // Compute real allocation size: Payload + BlockDescriptor.
+ unsigned real_size = size + sizeof(struct BlockDescriptor);
+
+ // No free block found in the list of blocks, allocate new block.
+ current = brk(real_size);
+
+ // Initialize new block.
+ current->mFree = 0;
+ current->mSize = size;
+ current->mNext = 0;
+
+ // Insert new block at the beginning of the list of blocks.
+ if (gHead != 0) {
+ current->mNext = gHead;
+ }
+ gHead = current;
+
+ return (void*)(current + 1);
+}
+
+void dealloc(void* ptr) {
+ // Get descriptor block.
+ struct BlockDescriptor* current = (struct BlockDescriptor*)ptr - 1;
+
+ // Mark block as free.
+ ERROR_ON(current->mFree, "Tried to de-alloc free block!");
+ current->mFree = 1;
+}
diff --git a/lib/src/common.c b/lib/src/common.c
new file mode 100644
index 0000000..dd806bf
--- /dev/null
+++ b/lib/src/common.c
@@ -0,0 +1,38 @@
+// Copyright (c) 2021 Johannes Stoelp
+
+#include <common.h>
+
+#if !defined(__linux__) || !defined(__x86_64__)
+# error "Only supported on linux(x86_64)!"
+#endif
+
+void* memset(void* s, int c, size_t n) {
+ asm volatile(
+ "cld"
+ "\n"
+ "rep stosb"
+ : "+D"(s), "+c"(n)
+ : "a"(c)
+ : "memory");
+ return s;
+}
+
+void* memcpy(void* d, const void* s, size_t n) {
+ // When `d` points into `[s, s+n[` we would override `s` while copying into `d`.
+ // |------------|--------|
+ // s d s+n
+ // -> We don't support.
+ //
+ // When `d` points into `]s-n, s[` it is destructive for `s` but all data
+ // from `s` are copied into `d`. The user gets what he asks for.
+ // -> Supported.
+ ERROR_ON(s <= d && d < (void*)((unsigned char*)s + n), "memcpy: Unsupported overlap!");
+ asm volatile(
+ "cld"
+ "\n"
+ "rep movsb"
+ : "+D"(d), "+S"(s), "+c"(n)
+ :
+ : "memory");
+ return d;
+}
diff --git a/lib/src/fmt.c b/lib/src/fmt.c
index be1ca3a..24ddd98 100644
--- a/lib/src/fmt.c
+++ b/lib/src/fmt.c
@@ -11,7 +11,7 @@ static const char* num2dec(char* buf, unsigned long len, unsigned long long num)
}
while (num > 0 && pbuf != buf) {
- char d = (num % 10) + '0';
+ char d = (char)(num % 10) + '0';
*(--pbuf) = d;
num /= 10;
}
@@ -28,7 +28,7 @@ static const char* num2hex(char* buf, unsigned long len, unsigned long long num)
while (num > 0 && pbuf != buf) {
char d = (num & 0xf);
- *(--pbuf) = d + (d > 9 ? 'a' - 10 : '0');
+ *(--pbuf) = (char)(d + (d > 9 ? 'a' - 10 : '0'));
num >>= 4;
}
return pbuf;
@@ -51,7 +51,7 @@ int vfmt(char* buf, unsigned long len, const char* fmt, va_list ap) {
put(*s++); \
}
- char scratch[16];
+ char scratch[32];
int l_cnt = 0;
while (*fmt) {
@@ -73,7 +73,7 @@ int vfmt(char* buf, unsigned long len, const char* fmt, va_list ap) {
val *= -1;
put('-');
}
- const char* ptr = num2dec(scratch, sizeof(scratch), val);
+ const char* ptr = num2dec(scratch, sizeof(scratch), (unsigned long)val);
puts(ptr);
} break;
case 'x': {
@@ -81,6 +81,10 @@ int vfmt(char* buf, unsigned long len, const char* fmt, va_list ap) {
const char* ptr = num2hex(scratch, sizeof(scratch), val);
puts(ptr);
} break;
+ case 'c': {
+ char c = va_arg(ap, int); // By C standard, value passed to varg smaller than `sizeof(int)` will be converted to int.
+ put(c);
+ } break;
case 's': {
const char* ptr = va_arg(ap, const char*);
puts(ptr);
diff --git a/lib/src/io.c b/lib/src/io.c
index b5e0dc5..5194042 100644
--- a/lib/src/io.c
+++ b/lib/src/io.c
@@ -1,10 +1,9 @@
// Copyright (c) 2020 Johannes Stoelp
-#include <io.h>
#include <fmt.h>
+#include <io.h>
#include <syscall.h>
-
-#include <asm/unistd.h>
+#include <syscalls.h>
// `pfmt` uses fixed-size buffer on the stack for formating the message
// (for simplicity and since we don't impl buffered I/O).
@@ -21,14 +20,14 @@ static int vdfmt(int fd, const char* fmt, va_list ap) {
int ret = vfmt(buf, sizeof(buf), fmt, ap);
if (ret > MAX_PRINTF_LEN - 1) {
- syscall3(__NR_write, fd, buf, MAX_PRINTF_LEN - 1);
+ write(fd, buf, MAX_PRINTF_LEN - 1);
static const char warn[] = "\npfmt: Message truncated, max length can be configured by defining MAX_PRINTF_LEN\n";
- syscall3(__NR_write, FD_STDERR, warn, sizeof(warn));
+ write(FD_STDERR, warn, sizeof(warn));
return MAX_PRINTF_LEN - 1;
}
- syscall3(__NR_write, fd, buf, ret);
+ write(fd, buf, ret);
return ret;
}
diff --git a/lib/src/syscalls.c b/lib/src/syscalls.c
new file mode 100644
index 0000000..074fced
--- /dev/null
+++ b/lib/src/syscalls.c
@@ -0,0 +1,62 @@
+// Copyright (c) 2021 Johannes Stoelp
+
+#include <asm/unistd.h> // __NR_*
+#include <syscall.h>
+#include <syscalls.h>
+
+// Storage for `dynld_errno`.
+int dynld_errno;
+
+// Convert return value to errno/ret.
+static long syscall_ret(unsigned long ret) {
+ if (ret > (unsigned long)-4096ul) {
+ dynld_errno = -ret;
+ return -1;
+ }
+ return ret;
+}
+
+int open(const char* path, int flags) {
+ long ret = syscall2(__NR_open, path, flags);
+ return syscall_ret(ret);
+}
+
+int close(int fd) {
+ long ret = syscall1(__NR_close, fd);
+ return syscall_ret(ret);
+}
+
+int access(const char* path, int mode) {
+ long ret = syscall2(__NR_access, path, mode);
+ return syscall_ret(ret);
+}
+
+ssize_t write(int fd, const void* buf, size_t count) {
+ long ret = syscall3(__NR_write, fd, buf, count);
+ return syscall_ret(ret);
+}
+
+ssize_t read(int fd, void* buf, size_t count) {
+ long ret = syscall3(__NR_read, fd, buf, count);
+ return syscall_ret(ret);
+}
+
+ssize_t pread(int fd, void* buf, size_t count, off_t offset) {
+ long ret = syscall4(__NR_read, fd, buf, count, offset);
+ return syscall_ret(ret);
+}
+
+void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset) {
+ long ret = syscall6(__NR_mmap, addr, length, prot, flags, fd, offset);
+ return (void*)syscall_ret(ret);
+}
+
+int munmap(void* addr, size_t length) {
+ long ret = syscall2(__NR_munmap, addr, length);
+ return syscall_ret(ret);
+}
+
+void _exit(int status) {
+ syscall1(__NR_exit, status);
+ __builtin_unreachable();
+}