From f30be3ddd5a4f52523379b5d179a4462c20aae17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Borna=20=C5=A0o=C5=A1tari=C4=87?= Date: Sat, 27 Dec 2025 11:19:42 +0100 Subject: [PATCH] initial implementation of klibc fix linker error about ctx_switch --- .gitignore | 2 + Makefile | 21 ++- .../{context_switch.s => context_switch.asm} | 2 +- kernel/memory.c | 123 +----------------- kernel/types.h | 1 - kernel/utils.c | 7 - kernel/utils.h | 2 + klibc/include/stdarg.h | 14 ++ klibc/include/stdbool.h | 6 + klibc/include/stddef.h | 10 ++ klibc/include/stdint.h | 16 +++ klibc/include/stdio.h | 4 + klibc/include/stdlib.h | 4 + klibc/include/string.h | 14 ++ klibc/src/string.c | 107 +++++++++++++++ 15 files changed, 198 insertions(+), 135 deletions(-) rename kernel/{context_switch.s => context_switch.asm} (98%) create mode 100644 klibc/include/stdarg.h create mode 100644 klibc/include/stdbool.h create mode 100644 klibc/include/stddef.h create mode 100644 klibc/include/stdint.h create mode 100644 klibc/include/stdio.h create mode 100644 klibc/include/stdlib.h create mode 100644 klibc/include/string.h create mode 100644 klibc/src/string.c diff --git a/.gitignore b/.gitignore index 7012c23..432f88b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ .build.env build cross +.cache/ +compile_commands.json diff --git a/Makefile b/Makefile index a99cafe..1787090 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,7 @@ QEMU= qemu-system-i386 OBJCOPY = i386-elf-objcopy BUILD_DIR = build +CROSS_DIR = cross DISK_IMG = $(BUILD_DIR)/disk.img STAGE2_SIZE = 2048 @@ -14,6 +15,9 @@ KERNEL_ASM_SRC = $(wildcard kernel/*.asm) KERNEL_OBJ = $(patsubst kernel/%.c, $(BUILD_DIR)/%.o, $(KERNEL_C_SRC)) KERNEL_OBJ += $(patsubst kernel/%.asm, $(BUILD_DIR)/asm_%.o, $(KERNEL_ASM_SRC)) +KLIBC_SRC = $(wildcard klibc/src/*.c) +KLIBC_OBJ = $(patsubst klibc/src/%.c, $(BUILD_DIR)/klibc/%.o, $(KLIBC_SRC)) + all: $(DISK_IMG) .PHONY: stage1 stage2 kernel run gdb clean @@ -26,7 +30,7 @@ stage1: $(BUILD_DIR) # Alternatively, convey the final stage2 size through other means to stage1. stage2: $(BUILD_DIR) $(AS) $(ASFLAGS) -o $(BUILD_DIR)/stage2.o bootloader/stage2.asm - $(CC) -std=c11 -ffreestanding -nostdlib -fno-stack-protector -m32 -g -c -o $(BUILD_DIR)/stage2_load.o bootloader/stage2_load.c + $(CC) -std=c11 -ffreestanding -nostdlib -nostdinc -fno-stack-protector -m32 -Iklibc/include -g -c -o $(BUILD_DIR)/stage2_load.o bootloader/stage2_load.c $(LD) -Tbootloader/stage2.ld -melf_i386 -o $(BUILD_DIR)/$@.elf $(BUILD_DIR)/stage2.o $(BUILD_DIR)/stage2_load.o $(OBJCOPY) -O binary $(BUILD_DIR)/$@.elf $(BUILD_DIR)/$@.bin truncate -s $(STAGE2_SIZE) $(BUILD_DIR)/$@.bin @@ -35,10 +39,13 @@ $(BUILD_DIR)/asm_%.o: kernel/%.asm $(AS) $(ASFLAGS) -o $@ $< $(BUILD_DIR)/%.o: kernel/%.c - $(CC) -std=c11 -ffreestanding -nostdlib -fno-stack-protector -m32 -g -c -o $@ $< + $(CC) -std=c11 -ffreestanding -nostdlib -nostdinc -fno-stack-protector -m32 -Iklibc/include -g -c -o $@ $< -kernel: $(KERNEL_OBJ) | $(BUILD_DIR) - $(LD) -melf_i386 -Tkernel/linker.ld -o $(BUILD_DIR)/kernel.elf $(KERNEL_OBJ) +$(BUILD_DIR)/klibc/%.o: klibc/src/%.c + $(CC) -std=c11 -ffreestanding -nostdlib -nostdinc -fno-stack-protector -m32 -Iklibc/include -g -c -o $@ $< + +kernel: $(KERNEL_OBJ) | $(BUILD_DIR) $(KLIBC_OBJ) + $(LD) -melf_i386 -Tkernel/linker.ld -o $(BUILD_DIR)/kernel.elf $(KERNEL_OBJ) $(KLIBC_OBJ) $(DISK_IMG): stage1 stage2 kernel dd if=$(BUILD_DIR)/stage1.bin of=$@ @@ -48,6 +55,7 @@ $(DISK_IMG): stage1 stage2 kernel $(BUILD_DIR): mkdir -p $@ + mkdir -p $(BUILD_DIR)/klibc run: qemu-system-i386 -s -S $(DISK_IMG) @@ -57,4 +65,9 @@ gdb: clean: rm -rf $(BUILD_DIR) + +clean-cross: rm -rf $(CROSS_DIR) + rm -rf .build.env + +clean-all: clean clean-cross diff --git a/kernel/context_switch.s b/kernel/context_switch.asm similarity index 98% rename from kernel/context_switch.s rename to kernel/context_switch.asm index 0e68648..89a910d 100644 --- a/kernel/context_switch.s +++ b/kernel/context_switch.asm @@ -1,4 +1,4 @@ -.global ctx_switch +global ctx_switch ; void ctx_switch(uint32_t **old_sp_ptr, uint32_t *new_sp); ; Arguments on stack (cdecl convention): diff --git a/kernel/memory.c b/kernel/memory.c index 9f4d02e..dc6aae2 100644 --- a/kernel/memory.c +++ b/kernel/memory.c @@ -1,6 +1,6 @@ #include "memory.h" -/* note: this is a stub, please use care as theres duplicate functions in utils implementation +/* note: this is a stub, please use care as theres duplicate functions in utils implementation /* --------------------------------------------------------------------- * * Helper: copy a single byte (used by both memcpy and memmove) * --------------------------------------------------------------------- */ @@ -15,124 +15,3 @@ static inline void byte_copy_backward(uint8_t *dst, const uint8_t *src, size_t n while (n--) *--dst = *--src; } -/* --------------------------------------------------------------------- * - * memcpy – no overlap allowed (behaviour undefined if overlap) - * --------------------------------------------------------------------- */ -void *memcpy(void *restrict dst, const void *restrict src, size_t n) -{ - uint8_t *d = (uint8_t *)dst; - const uint8_t *s = (const uint8_t *)src; - -#if defined(MEMORY_OPTIMIZED) - /* Align destination to 4-byte boundary */ - size_t align = (uintptr_t)d & 3U; - if (align) { - size_t head = 4 - align; - if (head > n) head = n; - byte_copy_forward(d, s, head); - d += head; s += head; n -= head; - } - - /* 32-bit word copy – safe because we already aligned dst */ - { - uint32_t *d32 = (uint32_t *)d; - const uint32_t *s32 = (const uint32_t *)s; - size_t words = n / 4; - while (words--) *d32++ = *s32++; - d = (uint8_t *)d32; - s = (const uint8_t *)s32; - n &= 3; - } -#endif - - byte_copy_forward(d, s, n); - return dst; -} - -/* --------------------------------------------------------------------- * - * memmove – handles overlapping regions correctly - * --------------------------------------------------------------------- */ -void *memmove(void *dst, const void *src, size_t n) -{ - uint8_t *d = (uint8_t *)dst; - const uint8_t *s = (const uint8_t *)src; - - if (n == 0 || dst == src) - return dst; - - if (d < s) { /* copy forward */ -#if defined(MEMORY_OPTIMIZED) - /* Same fast path as memcpy when no overlap */ - size_t align = (uintptr_t)d & 3U; - if (align) { - size_t head = 4 - align; - if (head > n) head = n; - byte_copy_forward(d, s, head); - d += head; s += head; n -= head; - } - { - uint32_t *d32 = (uint32_t *)d; - const uint32_t *s32 = (const uint32_t *)s; - size_t words = n / 4; - while (words--) *d32++ = *s32++; - d = (uint8_t *)d32; - s = (const uint8_t *)s32; - n &= 3; - } -#endif - byte_copy_forward(d, s, n); - } else { /* copy backward */ - byte_copy_backward(d, s, n); - } - return dst; -} - -/* --------------------------------------------------------------------- * - * memcmp – lexicographical compare - * --------------------------------------------------------------------- */ -int memcmp(const void *s1, const void *s2, size_t n) -{ - const uint8_t *a = (const uint8_t *)s1; - const uint8_t *b = (const uint8_t *)s2; - -#if defined(MEMORY_OPTIMIZED) - /* Align to 4-byte boundary */ - size_t align = (uintptr_t)a & 3U; - if (align && align == ((uintptr_t)b & 3U)) { - size_t head = 4 - align; - if (head > n) head = n; - while (head--) { - int diff = *a++ - *b++; - if (diff) return diff; - } - n -= head; - } - - { - const uint32_t *a32 = (const uint32_t *)a; - const uint32_t *b32 = (const uint32_t *)b; - size_t words = n / 4; - while (words--) { - uint32_t va = *a32++, vb = *b32++; - if (va != vb) { - /* byte-wise fallback for the differing word */ - const uint8_t *pa = (const uint8_t *)(a32 - 1); - const uint8_t *pb = (const uint8_t *)(b32 - 1); - for (int i = 0; i < 4; ++i) { - int diff = pa[i] - pb[i]; - if (diff) return diff; - } - } - } - a = (const uint8_t *)a32; - b = (const uint8_t *)b32; - n &= 3; - } -#endif - - while (n--) { - int diff = *a++ - *b++; - if (diff) return diff; - } - return 0; -} diff --git a/kernel/types.h b/kernel/types.h index 32fc7ff..332aa4e 100644 --- a/kernel/types.h +++ b/kernel/types.h @@ -27,7 +27,6 @@ typedef enum { false = 0, true = 1 } bool; // ---------------------------- // OS subsystem types // ---------------------------- -typedef uint32_t size_t; typedef int32_t ssize_t; typedef uint32_t phys_addr_t; // Physical address diff --git a/kernel/utils.c b/kernel/utils.c index 0cb4b04..a0f2035 100644 --- a/kernel/utils.c +++ b/kernel/utils.c @@ -76,10 +76,3 @@ char* utoa(unsigned int value, char* str, int base) { reverse(str, i); return str; } - -void *memset(void *dest, int value, size_t len) { - unsigned char *ptr = (unsigned char *)dest; - while (len-- > 0) - *ptr++ = (unsigned char)value; - return dest; -} diff --git a/kernel/utils.h b/kernel/utils.h index 802db8c..53fab8a 100644 --- a/kernel/utils.h +++ b/kernel/utils.h @@ -1,6 +1,8 @@ #ifndef UTILS_H #define UTILS_H +#include + #include "types.h" // Convert integer to string (base is typically 10, 16, etc.) diff --git a/klibc/include/stdarg.h b/klibc/include/stdarg.h new file mode 100644 index 0000000..07d50ca --- /dev/null +++ b/klibc/include/stdarg.h @@ -0,0 +1,14 @@ +#ifndef CLASSICOS_KLIBC_STDARG_H +#define CLASSICOS_KLIBC_STDARG_H + +typedef __builtin_va_list va_list; + +#ifndef va_start +#define va_start(ap, param) __builtin_va_start(ap, param) +#endif + +#define va_end(ap) __builtin_va_end(ap) +#define va_arg(ap, type) __builtin_va_arg(ap, type) +#define va_copy(dest, src) __builtin_va_copy(dest, src) + +#endif // CLASSICOS_KLIBC_STDARG_H diff --git a/klibc/include/stdbool.h b/klibc/include/stdbool.h new file mode 100644 index 0000000..0cd5393 --- /dev/null +++ b/klibc/include/stdbool.h @@ -0,0 +1,6 @@ +#ifndef CLASSICOS_KLIBC_STDBOOL_H +#define CLASSICOS_KLIBC_STDBOOL_H + +typedef enum { false = 0, true = 1 } bool; + +#endif // CLASSICOS_KLIBC_STDBOOL_H diff --git a/klibc/include/stddef.h b/klibc/include/stddef.h new file mode 100644 index 0000000..3b67bd7 --- /dev/null +++ b/klibc/include/stddef.h @@ -0,0 +1,10 @@ +#ifndef CLASSICOS_KLIBC_STDDEF_H +#define CLASSICOS_KLIBC_STDDEF_H + +typedef __SIZE_TYPE__ size_t; +typedef __PTRDIFF_TYPE__ ptrdiff_t; + +#undef NULL +#define NULL ((void*)0) + +#endif // CLASSICOS_KLIBC_STDDEF_H diff --git a/klibc/include/stdint.h b/klibc/include/stdint.h new file mode 100644 index 0000000..a0db679 --- /dev/null +++ b/klibc/include/stdint.h @@ -0,0 +1,16 @@ +#ifndef CLASSICOS_KLIBC_STDINT_H +#define CLASSICOS_KLIBC_STDINT_H + +typedef signed char int8_t; +typedef short int int16_t; +typedef int int32_t; +typedef long long int int64_t; + +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long int uint64_t; + +typedef unsigned int uintptr_t; + +#endif // CLASSICOS_KLIBC_STDINT_H diff --git a/klibc/include/stdio.h b/klibc/include/stdio.h new file mode 100644 index 0000000..af112bf --- /dev/null +++ b/klibc/include/stdio.h @@ -0,0 +1,4 @@ +#ifndef CLASSICOS_KLIBC_STDIO_H +#define CLASSICOS_KLIBC_STDIO_H + +#endif // CLASSICOS_KLIBC_STDIO_H diff --git a/klibc/include/stdlib.h b/klibc/include/stdlib.h new file mode 100644 index 0000000..5046538 --- /dev/null +++ b/klibc/include/stdlib.h @@ -0,0 +1,4 @@ +#ifndef CLASSICOS_KLIBC_STDLIB_H +#define CLASSICOS_KLIBC_STDLIB_H + +#endif // CLASSICOS_KLIBC_STDLIB_H diff --git a/klibc/include/string.h b/klibc/include/string.h new file mode 100644 index 0000000..4f01fc1 --- /dev/null +++ b/klibc/include/string.h @@ -0,0 +1,14 @@ +#ifndef CLASSICOS_KLIBC_STRING_H +#define CLASSICOS_KLIBC_STRING_H + +#include + +extern int memcmp(const void* s1, const void* s2, size_t n); +extern void* memmove(void* dst, const void* src, size_t n); +extern void* memcpy(void* dst, const void* src, size_t n); +extern void* memset(void* dst, int c, size_t n); + +extern size_t strlen(const char* s); +extern int strcmp(const char* s1, const char* s2); + +#endif // CLASSICOS_KLIBC_STRING_H diff --git a/klibc/src/string.c b/klibc/src/string.c new file mode 100644 index 0000000..a9a8a84 --- /dev/null +++ b/klibc/src/string.c @@ -0,0 +1,107 @@ +#include + +int memcmp(const void* s1, const void* s2, size_t n) { + const unsigned char* c1 = s1; + const unsigned char* c2 = s2; + int d = 0; + + while (n--) { + d = (int)*c1++ - (int)*c2++; + if (d) break; + } + + return d; +} + +void* memmove(void* dst, const void* src, size_t n) { + const char* p = src; + char* q = dst; +#if defined(__i386__) || defined(__x86_64__) + if (q < p) { + __asm__ volatile("cld; rep; movsb" : "+c"(n), "+S"(p), "+D"(q)); + } else { + p += (n - 1); + q += (n - 1); + __asm__ volatile("std; rep; movsb; cld" : "+c"(n), "+S"(p), "+D"(q)); + } +#else + if (q < p) { + while (n--) { + *q++ = *p++; + } + } else { + p += n; + q += n; + while (n--) { + *--q = *--p; + } + } +#endif + + return dst; +} + +void* memcpy(void* dst, const void* src, size_t n) { + const char* p = src; + char* q = dst; +#if defined(__i386__) + size_t nl = n >> 2; + __asm__ volatile("cld ; rep ; movsl ; movl %3,%0 ; rep ; movsb" + : "+c"(nl), "+S"(p), "+D"(q) + : "r"(n & 3)); +#elif defined(__x86_64__) + size_t nq = n >> 3; + __asm__ volatile("cld ; rep ; movsq ; movl %3,%%ecx ; rep ; movsb" + : "+c"(nq), "+S"(p), "+D"(q) + : "r"((uint32_t)(n & 7))); +#else + while (n--) { + *q++ = *p++; + } +#endif + + return dst; +} + +void* memset(void* dst, int c, size_t n) { + char* q = dst; + +#if defined(__i386__) + size_t nl = n >> 2; + __asm__ volatile("cld ; rep ; stosl ; movl %3,%0 ; rep ; stosb" + : "+c"(nl), "+D"(q) + : "a"((unsigned char)c * 0x01010101U), "r"(n & 3)); +#elif defined(__x86_64__) + size_t nq = n >> 3; + __asm__ volatile("cld ; rep ; stosq ; movl %3,%%ecx ; rep ; stosb" + : "+c"(nq), "+D"(q) + : "a"((unsigned char)c * 0x0101010101010101U), + "r"((uint32_t)n & 7)); +#else + while (n--) { + *q++ = c; + } +#endif + + return dst; +} + +size_t strlen(const char* s) { + const char* ss = s; + while (*ss) ss++; + return ss - s; +} + +int strcmp(const char* s1, const char* s2) { + const unsigned char* c1 = (const unsigned char*)s1; + const unsigned char* c2 = (const unsigned char*)s2; + unsigned char ch; + int d = 0; + + while (1) { + d = (int)(ch = *c1++) - (int)*c2++; + if (d || !ch) break; + } + + return d; +}