From ba6ff0c94a5626a67f4fff5e9e7e1a1d7e9d6e30 Mon Sep 17 00:00:00 2001 From: csd4ni3l Date: Tue, 7 Apr 2026 22:59:49 +0200 Subject: [PATCH] Upload current libxunil, to be used as a submodule --- .cargo/config.toml | 4 + .gitignore | 1 + Cargo.lock | 34 +++ Cargo.toml | 18 ++ include/inttypes.h | 1 + include/stdio.h | 44 +++ include/stdlib.h | 24 ++ include/string.h | 22 ++ include/strings.h | 8 + rust-toolchain.toml | 8 + src/file.rs | 255 +++++++++++++++++ src/heap.rs | 109 ++++++++ src/lib.rs | 662 ++++++++++++++++++++++++++++++++++++++++++++ src/mem.rs | 258 +++++++++++++++++ src/syscall.rs | 77 ++++++ src/time.rs | 38 +++ src/util.rs | 20 ++ 17 files changed, 1583 insertions(+) create mode 100644 .cargo/config.toml create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 include/inttypes.h create mode 100644 include/stdio.h create mode 100644 include/stdlib.h create mode 100644 include/string.h create mode 100644 include/strings.h create mode 100644 rust-toolchain.toml create mode 100644 src/file.rs create mode 100644 src/heap.rs create mode 100644 src/lib.rs create mode 100644 src/mem.rs create mode 100644 src/syscall.rs create mode 100644 src/time.rs create mode 100644 src/util.rs diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..7dc1635 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,4 @@ +[unstable] + json-target-spec = true + build-std-features = ["compiler-builtins-mem"] + build-std = ["core", "compiler_builtins", "alloc"] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..13f6007 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,34 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "libxunil" +version = "0.1.0" +dependencies = [ + "spin", +] + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "spin" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" +dependencies = [ + "lock_api", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..eadb2ff --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "libxunil" +version = "0.1.0" +edition = "2024" + +[lib] +name = "xunil" +crate-type = ["staticlib"] + +[profile.release] +panic = "abort" +opt-level = "s" + +[dependencies] +spin = "0.10.0" + +[profile.dev] +panic = "abort" diff --git a/include/inttypes.h b/include/inttypes.h new file mode 100644 index 0000000..9a6118b --- /dev/null +++ b/include/inttypes.h @@ -0,0 +1 @@ +#include diff --git a/include/stdio.h b/include/stdio.h new file mode 100644 index 0000000..dbfb286 --- /dev/null +++ b/include/stdio.h @@ -0,0 +1,44 @@ +#pragma once +#include +#include +#include + +typedef struct FILE FILE; + +extern FILE *stdin; +extern FILE *stdout; +extern FILE *stderr; + +#define EOF (-1) + +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 + +FILE *fopen(const char *path, const char *mode); +int fclose(FILE *fp); +size_t fread(void *ptr, size_t size, size_t nmemb, FILE *fp); +size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *fp); +int fseek(FILE *fp, long offset, int whence); +long ftell(FILE *fp); +int fflush(FILE *fp); +char *fgets(char *s, int size, FILE *fp); +int fputs(const char *s, FILE *fp); +int feof(FILE *fp); +int ferror(FILE *fp); +int remove(const char *path); +int rename(const char *path, const char *new_path); + +int printf(const char *fmt, ...); +int puts(const char *s); +int putchar(int c); + +int fprintf(FILE *fp, const char *fmt, ...); +int sprintf(char *buf, const char *fmt, ...); +int snprintf(char *buf, size_t size, const char *fmt, ...); +int vsnprintf(char *buf, size_t size, const char *fmt, va_list ap); +int vfprintf(FILE *fp, const char *fmt, va_list ap); +ssize_t write(int fd, const void *buf, size_t count); +void exit(int code); + +int sscanf(const char *str, const char *format, ...); diff --git a/include/stdlib.h b/include/stdlib.h new file mode 100644 index 0000000..b32dd28 --- /dev/null +++ b/include/stdlib.h @@ -0,0 +1,24 @@ +#pragma once +#include +#include + +void *malloc(size_t size); +void *calloc(size_t nmemb, size_t size); +void *realloc(void *ptr, size_t size); +void free(void *ptr); + +void exit(int status); +void abort(void); + +int atoi(const char *s); +long atol(const char *s); +double atof(const char *s); +long strtol(const char *s, char **endptr, int base); +double strtod(const char *s, char **endptr); + +char *getenv(const char *name); +void qsort(void *base, size_t nmemb, size_t size, int (*cmp)(const void *, const void *)); +int abs(int x); +int system(const char *cmd); +int draw_pixel(uint32_t x, uint32_t y, uint32_t color); +int framebuffer_swap(); diff --git a/include/string.h b/include/string.h new file mode 100644 index 0000000..c4209c4 --- /dev/null +++ b/include/string.h @@ -0,0 +1,22 @@ +#pragma once +#include + +void *memset(void *s, int c, size_t n); +void *memcpy(void *dst, const void *src, size_t n); +void *memmove(void *dst, const void *src, size_t n); +int memcmp(const void *s1, const void *s2, size_t n); +void *memchr(const void *s, int c, size_t n); + +size_t strlen(const char *s); +char *strcpy(char *dst, const char *src); +char *strncpy(char *dst, const char *src, size_t n); +char *strcat(char *dst, const char *src); +char *strncat(char *dst, const char *src, size_t n); +int strcmp(const char *s1, const char *s2); +int strncmp(const char *s1, const char *s2, size_t n); +char *strchr(const char *s, int c); +char *strrchr(const char *s, int c); +char *strstr(const char *haystack, const char *needle); +char *strtok(char *str, const char *delim); +char *strdup(const char *s); +char *strerror(int errnum); diff --git a/include/strings.h b/include/strings.h new file mode 100644 index 0000000..5abbdee --- /dev/null +++ b/include/strings.h @@ -0,0 +1,8 @@ +#pragma once +#include + +int strcasecmp(const char *s1, const char *s2); +int strncasecmp(const char *s1, const char *s2, size_t n); +void bzero(void *s, size_t n); +void bcopy(const void *src, void *dest, size_t n); +int bcmp(const void *s1, const void *s2, size_t n); diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..66a0077 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,8 @@ +[toolchain] +channel = "nightly" +targets = [ + "x86_64-unknown-none", + # "aarch64-unknown-none", + # "riscv64gc-unknown-none-elf", + # "loongarch64-unknown-none", +] diff --git a/src/file.rs b/src/file.rs new file mode 100644 index 0000000..2a87d75 --- /dev/null +++ b/src/file.rs @@ -0,0 +1,255 @@ +use core::{ + ffi::CStr, + ptr::{null, null_mut}, +}; + +use crate::{mem::free, printf, puts}; + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct FILE { + pub data: *const u8, // pointer to the file's data + pub size: usize, // total size + pub cursor: usize, // current position + pub writable: bool, // is this a write buffer? + pub write_buf: *mut u8, // for writable fake files + pub write_cap: usize, + pub fd: i64, +} + +impl FILE { + pub const fn zeroed() -> FILE { + FILE { + data: null(), + size: 0, + cursor: 0, + writable: false, + write_buf: null_mut(), + write_cap: 0, + fd: -1, + } + } +} + +struct FakeFileEntry { + name: &'static str, + data: &'static [u8], +} + +#[repr(C, align(8))] +struct AlignedWAD([u8; include_bytes!("../../../assets/doom1.wad").len()]); +static TEST_WAD: AlignedWAD = AlignedWAD(*include_bytes!("../../../assets/doom1.wad")); +static TEST_WAD_BYTES: &[u8] = &TEST_WAD.0; + +static FILES: &[FakeFileEntry] = &[ + FakeFileEntry { + name: "doom1.wad", + data: TEST_WAD_BYTES, + }, + FakeFileEntry { + name: "default.cfg", + data: b"", + }, + FakeFileEntry { + name: "doom.cfg", + data: b"", + }, +]; + +static mut FILE_POOL: [FILE; 16] = [FILE::zeroed(); 16]; +static mut FILE_POOL_USED: [bool; 16] = [false; 16]; +static mut STDERR_FILE: FILE = FILE::zeroed(); +static mut STDOUT_FILE: FILE = FILE::zeroed(); +static mut STDIN_FILE: FILE = FILE::zeroed(); + +#[unsafe(no_mangle)] +pub static mut stderr: *mut FILE = unsafe { &raw mut STDERR_FILE }; +#[unsafe(no_mangle)] +pub static mut stdin: *mut FILE = unsafe { &raw mut STDIN_FILE }; +#[unsafe(no_mangle)] +pub static mut stdout: *mut FILE = unsafe { &raw mut STDOUT_FILE }; + +pub unsafe fn get_file_pool_slot() -> (*mut FILE, i64) { + unsafe { + for i in 0..16 { + if !FILE_POOL_USED[i] { + FILE_POOL_USED[i] = true; + return (&mut FILE_POOL[i], i as i64); + } + } + + (null_mut(), -1) + } +} + +#[unsafe(no_mangle)] +extern "C" fn fopen(path: *const i8, mode: *const i8) -> *mut FILE { + if path.is_null() || mode.is_null() { + return null_mut(); + } + + unsafe { + let name = CStr::from_ptr(path).to_str().unwrap_or(""); + + for entry in FILES { + if name.contains(entry.name) { + let (slot, fd) = get_file_pool_slot(); + + if slot.is_null() { + return null_mut(); + } + + (*slot).data = entry.data.as_ptr(); + (*slot).size = entry.data.len(); + (*slot).cursor = 0; + (*slot).writable = false; + (*slot).write_buf = null_mut(); + (*slot).write_cap = 0; + (*slot).fd = fd; + return slot; + } + } + } + + null_mut() +} + +#[unsafe(no_mangle)] +extern "C" fn fclose(file_ptr: *mut FILE) -> i32 { + if file_ptr.is_null() || unsafe { (*file_ptr).fd < 0 || (*file_ptr).fd >= 16 } { + return -1; + } + + unsafe { FILE_POOL_USED[(*file_ptr).fd as usize] = false }; + unsafe { *file_ptr = FILE::zeroed() }; + + 0 +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn fprintf(file_ptr: *mut FILE, fmt: *const u8, args: ...) -> i32 { + if fmt.is_null() || file_ptr.is_null() || unsafe { (*file_ptr).fd < 0 || (*file_ptr).fd >= 16 } + { + return -1; + } + + 0 +} + +#[unsafe(no_mangle)] +extern "C" fn fread(ptr: *mut u8, size: usize, nmemb: usize, fp: *mut FILE) -> usize { + if size == 0 + || nmemb == 0 + || ptr.is_null() + || fp.is_null() + || unsafe { (*fp).fd < 0 || (*fp).fd >= 16 } + { + return 0; + } + + let total = match size.checked_mul(nmemb) { + Some(t) => t, + None => return 0, + }; + + unsafe { + let f = &mut *fp; + if f.cursor > f.size { + puts(b"failed to read\0".as_ptr()); + return 0; + } + + let available = f.size - f.cursor; + let to_read = total.min(available); + + if to_read > 0 { + core::ptr::copy_nonoverlapping(f.data.add(f.cursor), ptr, to_read); + f.cursor = f.cursor.saturating_add(to_read); + } + + to_read / size + } +} + +#[unsafe(no_mangle)] +extern "C" fn fseek(stream: *mut FILE, offset: i64, whence: i32) -> i32 { + if stream.is_null() || unsafe { (*stream).fd } == -1 { + return -1; + } + + let f = unsafe { &mut *stream }; + + let new_pos = match whence { + 0 => { + if offset < 0 { + return -1; + } + offset as usize + } + 1 => { + let cur = f.cursor as i64; + let pos = cur.saturating_add(offset); + if pos < 0 { + return -1; + } + pos as usize + } + 2 => { + let end = f.size as i64; + let pos = end.saturating_add(offset); + if pos < 0 { + return -1; + } + pos as usize + } + _ => return -1, + }; + + if new_pos > f.size { + return -1; + } + + f.cursor = new_pos; + 0 +} + +#[unsafe(no_mangle)] +extern "C" fn fwrite(ptr: *mut u8, size: usize, count: usize, fp: *mut FILE) -> usize { + if ptr.is_null() || fp.is_null() || unsafe { (*fp).fd < 0 || (*fp).fd >= 16 } { + return 0; + } + count +} + +#[unsafe(no_mangle)] +extern "C" fn ftell(stream: *mut FILE) -> i64 { + if stream.is_null() || unsafe { (*stream).fd < 0 || (*stream).fd >= 16 } { + return -1; + } + unsafe { (*stream).cursor as i64 } +} + +#[unsafe(no_mangle)] +extern "C" fn fflush(file_ptr: *mut FILE) -> i32 { + 0 +} + +#[unsafe(no_mangle)] +extern "C" fn mkdir(path: *const u8, mode: *const u8) -> i32 { + 0 +} + +#[unsafe(no_mangle)] +extern "C" fn remove(path: *const i8) -> i32 { + 0 +} + +#[unsafe(no_mangle)] +extern "C" fn rename(path: *const u8, new_path: *const u8) -> i32 { + 0 +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn vfprintf(stream: *const u8, format: *const u8, args: ...) -> i32 { + 0 +} diff --git a/src/heap.rs b/src/heap.rs new file mode 100644 index 0000000..88355e7 --- /dev/null +++ b/src/heap.rs @@ -0,0 +1,109 @@ +use spin::mutex::Mutex; + +use crate::util::align_up; + +pub struct LinkedNode { + pub size: usize, + pub next: Option<&'static mut LinkedNode>, +} + +impl LinkedNode { + pub const fn new(size: usize) -> LinkedNode { + LinkedNode { size, next: None } + } + + pub fn start_addr(&self) -> usize { + self as *const Self as usize + } + + pub fn end_addr(&self) -> usize { + self.start_addr() + self.size + } +} + +pub struct LinkedListAllocator { + head: LinkedNode, +} + +#[derive(Clone, Copy)] +pub struct Allocation { + pub start: usize, + pub end: usize, +} + +impl LinkedListAllocator { + pub const fn new() -> LinkedListAllocator { + Self { + head: LinkedNode::new(0), + } + } + + pub unsafe fn add_free_memory_region(&mut self, start: usize, size: usize) { + unsafe { + assert_eq!(align_up(start, 16), start); + assert!(size >= core::mem::size_of::()); + + let mut node = LinkedNode::new(size); + node.next = self.head.next.take(); + + let node_ptr = start as *mut LinkedNode; + node_ptr.write(node); + self.head.next = Some(&mut *node_ptr); + } + } + + pub fn find_region(&mut self, size: usize) -> Option { + let mut current = &mut self.head; + + while let Some(ref region) = current.next { + let mut alloc = match Self::alloc_from_region(region, size) { + Ok(a) => a, + Err(()) => { + current = current.next.as_mut().unwrap(); + continue; + } + }; + + let taken = current.next.take().unwrap(); + let region_end = taken.end_addr(); + + let old_next = unsafe { + let node_ptr = taken as *mut LinkedNode; + let next_ptr = core::ptr::addr_of_mut!((*node_ptr).next); + core::ptr::read(next_ptr) + }; + + let excess_size = region_end - alloc.end; + + if excess_size >= core::mem::size_of::() { + unsafe { + let remainder_ptr = alloc.end as *mut LinkedNode; + remainder_ptr.write(LinkedNode { + size: excess_size, + next: old_next, + }); + current.next = Some(&mut *remainder_ptr); + } + } else { + alloc.end = region_end; + current.next = old_next; + } + + return Some(alloc); + } + + None + } + + fn alloc_from_region(region: &LinkedNode, size: usize) -> Result { + let start = region.start_addr(); + let end_unaligned = start.checked_add(size).ok_or(())?; + let end = align_up(end_unaligned, 16); + if end > region.end_addr() { + return Err(()); + } + Ok(Allocation { start, end }) + } +} + +pub static ALLOCATOR: Mutex = Mutex::new(LinkedListAllocator::new()); diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..a55ea5c --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,662 @@ +#![no_std] +#![feature(c_variadic)] +use core::{ + ffi::VaList, + fmt::{Error, Result, Write}, + ptr::{addr_of_mut, null, null_mut}, + usize, +}; + +use crate::{ + mem::{malloc, memcpy}, + syscall::{DRAW_BUFFER, DRAW_PIXEL, EXIT, FRAMEBUFFER_SWAP, WRITE, syscall0, syscall3}, +}; + +pub mod file; +pub mod heap; +pub mod mem; +pub mod syscall; +pub mod time; +pub mod util; + +static mut ERRNO: core::ffi::c_int = 0; + +static TOUPPER_TABLE: [i32; 384] = { + let mut table = [0i32; 384]; + let mut i = 0usize; + while i < 384 { + let c = i.wrapping_sub(128) as u8; + table[i] = if c.is_ascii_lowercase() { + (c - 0x20) as i32 + } else { + c as i32 + }; + i += 1; + } + table +}; + +#[unsafe(no_mangle)] +extern "C" fn write(fd: i32, buf: *const u8, count: usize) -> isize { + unsafe { syscall3(WRITE, fd as isize, buf as isize, count as isize) } +} + +#[unsafe(no_mangle)] +extern "C" fn exit(code: i32) -> ! { + unsafe { syscall3(EXIT, code as isize, 0, 0) }; + loop { + unsafe { core::arch::asm!("nop") }; + } +} + +#[unsafe(no_mangle)] +extern "C" fn strlen(s: *const u8) -> usize { + let mut len = 0usize; + while unsafe { *s.add(len) } != 0 { + len += 1; + } + len +} + +#[unsafe(no_mangle)] +extern "C" fn puts(s: *const u8) -> i32 { + write(1, s, strlen(s)); + write(1, b"\n\0".as_ptr(), 1); + + 0 +} + +#[unsafe(no_mangle)] +extern "C" fn putchar(c: i32) -> i32 { + let b = c as u8; + write(1, core::ptr::addr_of!(b), 1); + + 0 +} + +#[unsafe(no_mangle)] +extern "C" fn abs(n: i32) -> i32 { + n.abs() +} + +struct BufWriter { + buf: *mut u8, + max: usize, + pos: usize, +} + +impl Write for BufWriter { + fn write_str(&mut self, s: &str) -> Result { + for byte in s.bytes() { + if self.pos >= self.max { + return Err(Error); + } + unsafe { + *self.buf.add(self.pos) = byte; + } + self.pos += 1; + } + Ok(()) + } +} + +struct StdoutWriter { + size: usize, +} + +impl Write for StdoutWriter { + fn write_str(&mut self, s: &str) -> Result { + self.size += s.len(); + write(1, s.as_ptr(), s.len()); + Ok(()) + } +} + +pub unsafe fn write_c_formatted(fmt: *const u8, args: &mut VaList, writer: &mut impl Write) { + let mut fi = 0usize; + + loop { + let ch = unsafe { *fmt.add(fi) }; + fi += 1; + if ch == 0 { + break; + } + + if ch != b'%' { + let _ = writer.write_char(ch as char); + continue; + } + + let mut precision: Option = None; + let mut next_byte = unsafe { *fmt.add(fi) }; + + if next_byte == b'.' { + fi += 1; + let mut p_val = 0usize; + loop { + let digit = unsafe { *fmt.add(fi) }; + if digit >= b'0' && digit <= b'9' { + p_val = p_val * 10 + (digit - b'0') as usize; + fi += 1; + } else { + break; + } + } + precision = Some(p_val); + next_byte = unsafe { *fmt.add(fi) }; + } + + let spec = next_byte; + fi += 1; + + unsafe { + match spec { + b'd' | b'i' => { + let v: i32 = args.arg(); + if let Some(p) = precision { + let _ = write!(writer, "{:01$}", v, p); + } else { + let _ = write!(writer, "{}", v); + } + } + b'u' => { + let v: u32 = args.arg(); + if let Some(p) = precision { + let _ = write!(writer, "{:01$}", v, p); + } else { + let _ = write!(writer, "{}", v); + } + } + b'x' => { + let v: u32 = args.arg(); + if let Some(p) = precision { + let _ = write!(writer, "{:01$x}", v, p); + } else { + let _ = write!(writer, "{:x}", v); + } + } + b'X' => { + let v: u32 = args.arg(); + if let Some(p) = precision { + let _ = write!(writer, "{:01$X}", v, p); + } else { + let _ = write!(writer, "{:X}", v); + } + } + b'o' => { + let v: u32 = args.arg(); + if let Some(p) = precision { + let _ = write!(writer, "{:01$o}", v, p); + } else { + let _ = write!(writer, "{:o}", v); + } + } + b'p' => { + let v: *const u8 = args.arg(); + if v.is_null() { + let _ = writer.write_str("(null)"); + } else { + let _ = write!(writer, "0x{:x}", v as usize); + } + } + b'c' => { + let v: i32 = args.arg(); + let _ = writer.write_char((v as u8) as char); + } + b's' => { + let ptr: *const u8 = args.arg(); + if ptr.is_null() { + let _ = writer.write_str("(null)"); + } else { + let mut si = 0usize; + loop { + let c = *ptr.add(si); + if c == 0 { + break; + } + if let Some(p) = precision { + if si >= p { + break; + } + } + let _ = writer.write_char(c as char); + si += 1; + } + } + } + b'f' | b'F' | b'g' | b'G' => { + let v: f64 = args.arg(); + if let Some(p) = precision { + let _ = write!(writer, "{:.*}", p, v); + } else { + let _ = write!(writer, "{}", v); + } + } + b'l' => { + let next_spec = *fmt.add(fi); + fi += 1; + match next_spec { + b'd' | b'i' => { + let v: i64 = args.arg(); + if let Some(p) = precision { + let _ = write!(writer, "{:01$}", v, p); + } else { + let _ = write!(writer, "{}", v); + } + } + b'u' => { + let v: u64 = args.arg(); + if let Some(p) = precision { + let _ = write!(writer, "{:01$}", v, p); + } else { + let _ = write!(writer, "{}", v); + } + } + _ => { + let _ = writer.write_char('%'); + let _ = writer.write_char('l'); + let _ = writer.write_char(next_spec as char); + } + } + } + b'%' => { + let _ = writer.write_char('%'); + } + _ => { + let _ = writer.write_char('%'); + let _ = writer.write_char(spec as char); + } + } + } + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn printf(fmt: *const u8, mut args: ...) -> i32 { + let mut writer = StdoutWriter { size: 0 }; + + unsafe { write_c_formatted(fmt, &mut args, &mut writer) }; + + writer.size as i32 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn vsnprintf( + buf: *mut u8, + size: usize, + fmt: *const u8, + mut args: VaList, +) -> i32 { + if buf.is_null() || size == 0 || fmt.is_null() { + return -1; + } + + let max = size - 1; + let mut writer = BufWriter { buf, max, pos: 0 }; + + unsafe { write_c_formatted(fmt, &mut args, &mut writer) }; + + unsafe { + *buf.add(writer.pos) = 0; + } + + writer.pos as i32 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn snprintf(buf: *mut u8, size: usize, fmt: *const u8, mut args: ...) -> i32 { + if buf.is_null() || size == 0 || fmt.is_null() { + return -1; + } + + let max = size - 1; + let mut writer = BufWriter { buf, max, pos: 0 }; + + unsafe { write_c_formatted(fmt, &mut args, &mut writer) }; + + unsafe { + *buf.add(writer.pos) = 0; + } + + writer.pos as i32 +} + +unsafe extern "C" { + fn main(argc: i32, argv: *const *const u8) -> i32; +} + +#[unsafe(no_mangle)] +pub extern "C" fn __stack_chk_fail() -> ! { + exit(127) +} + +#[unsafe(no_mangle)] +pub extern "C" fn __stack_chk_fail_local() -> ! { + __stack_chk_fail() +} + +#[unsafe(no_mangle)] +pub extern "C" fn _start() -> ! { + let code = unsafe { main(0, null()) }; + + exit(code as i32); +} +#[unsafe(no_mangle)] +extern "C" fn atoi(mut c: *const u8) -> i32 { + let mut value: i32 = 0; + let mut sign: i32 = 1; + unsafe { + while (*c).is_ascii_whitespace() { + c = c.add(1); + } + + if (*c) == b'+' || (*c) == b'-' { + if *c == b'-' { + sign = -1; + } + c = c.add(1); + } + while (*c).is_ascii_digit() { + value *= 10; + value += ((*c) - b'0') as i32; + c = c.add(1); + } + } + + value * sign +} + +#[inline] +fn pow10_i32(exp: i32) -> f64 { + let mut e = exp; + let mut scale: f64 = 1.0; + + if e > 0 { + while e > 0 { + scale *= 10.0; + e -= 1; + } + } else if e < 0 { + while e < 0 { + scale *= 0.1; + e += 1; + } + } + + scale +} + +#[unsafe(no_mangle)] +extern "C" fn atof(mut c: *const u8) -> f64 { + let mut sign: f64 = 1.0; + unsafe { + while (*c).is_ascii_whitespace() { + c = c.add(1); + } + + if (*c) == b'+' || (*c) == b'-' { + if *c == b'-' { + sign = -1.0; + } + c = c.add(1); + } + + let mut int_part: i64 = 0; + while (*c).is_ascii_digit() { + int_part = int_part * 10 + ((*c) - b'0') as i64; + c = c.add(1); + } + + let mut result: f64 = int_part as f64; + + if *c == b'.' { + c = c.add(1); + let mut factor = 0.1; + while (*c).is_ascii_digit() { + result += ((*c) - b'0') as f64 * factor; + factor *= 0.1; + c = c.add(1); + } + } + + if *c == b'e' || *c == b'E' { + c = c.add(1); + + let mut exp_sign = 1; + let mut exp_value = 0; + + if (*c) == b'+' || (*c) == b'-' { + if *c == b'-' { + exp_sign = -1; + } + c = c.add(1); + } + + while (*c).is_ascii_digit() { + exp_value *= 10; + exp_value += ((*c) - b'0') as i64; + c = c.add(1); + } + + result *= pow10_i32((exp_sign * exp_value) as i32); + } + + sign * result + } +} + +pub fn compare_str(str_1: *const u8, str_2: *const u8, ignore_case: bool, n: usize) -> i32 { + let mut len = 0; + while len < n { + let mut c_1 = unsafe { *str_1.add(len) }; + let mut c_2 = unsafe { *str_2.add(len) }; + if ignore_case { + c_1 = c_1.to_ascii_lowercase(); + c_2 = c_2.to_ascii_lowercase(); + } + if c_1 != c_2 { + return (c_1 as i32) - (c_2 as i32); + } + if c_1 == 0 { + return 0; + } + len += 1; + } + 0 +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn strcasecmp(str_1: *const u8, str_2: *const u8) -> i32 { + compare_str(str_1, str_2, true, usize::MAX) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn strcmp(str_1: *const u8, str_2: *const u8) -> i32 { + compare_str(str_1, str_2, false, usize::MAX) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn strncasecmp(str_1: *const u8, str_2: *const u8, n: usize) -> i32 { + compare_str(str_1, str_2, true, n) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn strncmp(str_1: *const u8, str_2: *const u8, n: usize) -> i32 { + compare_str(str_1, str_2, false, n) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn draw_pixel(x: u32, y: u32, color: u32) -> i32 { + unsafe { + return syscall3(DRAW_PIXEL, x as isize, y as isize, color as isize) as i32; + } +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn draw_buffer(buffer: *const u32, width: u32, height: u32) -> i32 { + unsafe { + return syscall3( + DRAW_BUFFER, + buffer as isize, + width as isize, + height as isize, + ) as i32; + } +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn framebuffer_swap() -> i32 { + unsafe { + return syscall0(FRAMEBUFFER_SWAP) as i32; + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strncpy(dest: *mut u8, source: *const u8, n: usize) -> *mut u8 { + let mut i = 0usize; + while i < n { + let b = unsafe { *source.add(i) }; + unsafe { *dest.add(i) = b }; + + if b == 0 { + let mut j = i + 1; + while j < n { + unsafe { *dest.add(j) = 0 }; + j += 1; + } + break; + } + i += 1; + } + + dest +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn strdup(s: *const u8) -> *mut u8 { + let len = strlen(s); + let memory = malloc((len + 1) as u64); + if memory.is_null() { + return null_mut(); + } + unsafe { memcpy(memory, s, len + 1) }; + memory +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn strstr(haystack: *const u8, needle: *const u8) -> *const u8 { + if haystack.is_null() || needle.is_null() { + return null(); + } + + let mut h = haystack; + + unsafe { + if *needle == 0 { + return haystack; + } + + while *h != 0 { + if *h == *needle { + let mut h2 = h; + let mut n2 = needle; + + while *n2 != 0 && *h2 != 0 && *h2 == *n2 { + h2 = h2.add(1); + n2 = n2.add(1); + } + + if *n2 == 0 { + return h; + } + } + + h = h.add(1); + } + } + + null() +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn strchr(s: *const u8, ch: i32) -> *const u8 { + if s.is_null() { + return null(); + } + + let mut i = 0usize; + + unsafe { + while *s.add(i) != 0 { + if *s.add(i) == ch as u8 { + return s.add(i); + } + + i += 1; + } + } + + null() +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn strrchr(s: *const u8, ch: u8) -> *const u8 { + let mut n = 0; + let mut last: *const u8 = null(); + + if ch == 0 { + while unsafe { *s.add(n) } != 0 { + n += 1; + } + return unsafe { s.add(n + 1) }; + } else { + while unsafe { *s.add(n) } != 0 { + let cur_ch = unsafe { s.add(n) }; + if unsafe { *cur_ch == ch } { + last = cur_ch; + } + n += 1; + } + + last + } +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn toupper(char: i32) -> i32 { + (char as u8).to_ascii_uppercase() as i32 +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn tolower(char: i32) -> i32 { + (char as u8).to_ascii_lowercase() as i32 +} + +#[unsafe(no_mangle)] +extern "C" fn system(cmd: *const u8) -> i32 { + 0 +} +#[unsafe(no_mangle)] +extern "C" fn __ctype_toupper_loc() -> *const u8 { + TOUPPER_TABLE.as_ptr() as *const u8 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn __isoc23_sscanf() -> ! { + panic!("sscanf not implemented"); +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sscanf(str: *mut u8, fmt: *const u8, args: ...) -> i32 { + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn __errno_location() -> *mut core::ffi::c_int { + addr_of_mut!(ERRNO) +} + +#[panic_handler] +fn panic(error: &core::panic::PanicInfo) -> ! { + exit(-1) +} diff --git a/src/mem.rs b/src/mem.rs new file mode 100644 index 0000000..9b4f3f0 --- /dev/null +++ b/src/mem.rs @@ -0,0 +1,258 @@ +use core::{ffi::c_int, mem, ptr::null_mut, usize}; + +use crate::{ + heap::{ALLOCATOR, LinkedNode}, + syscall::{BRK, syscall1}, + util::align_up, +}; + +pub fn sbrk(increment: i64) -> isize { + unsafe { syscall1(BRK, increment as isize) as isize } +} + +const MAX_SIZE: u64 = 18446744073709551615; + +const HEADER_MAGIC_ALLOC: u64 = 0xBADC0FFEE0DDF00D; +const HEADER_MAGIC_FREE: u64 = 0xFEE1DEADCAFEBABE; + +#[repr(C, align(16))] +struct Header { + magic: u64, + _pad: u64, + alloc_size: usize, + region_size: usize, +} + +#[unsafe(no_mangle)] +pub extern "C" fn calloc(count: u64, size: u64) -> *mut u8 { + if count != 0 && size > MAX_SIZE / count { + return null_mut(); + } + + let mut total = count * size; + if total == 0 { + total = 1; + } + + let ptr = malloc(total); + if ptr.is_null() { + return null_mut(); + } + + unsafe { memset(ptr, 0, total as usize) }; + ptr +} + +#[unsafe(no_mangle)] +pub extern "C" fn free(ptr: *mut u8) { + if ptr.is_null() { + return; + } + + unsafe { + let header_ptr = (ptr as usize - mem::size_of::
()) as *mut Header; + if (header_ptr as usize) & 0xF != 0 { + core::hint::unreachable_unchecked(); + } + + if (*header_ptr).magic != HEADER_MAGIC_ALLOC { + return; + } + + let region_size = (*header_ptr).region_size; + (*header_ptr).magic = HEADER_MAGIC_FREE; + let mut allocator = ALLOCATOR.lock(); + allocator.add_free_memory_region(header_ptr as usize, region_size); + } +} + +#[unsafe(no_mangle)] +pub extern "C" fn malloc(size: u64) -> *mut u8 { + let req = size as usize; + let req = if req == 0 { 1 } else { req }; + + let hdr = mem::size_of::
(); + let needed_unaligned = match hdr.checked_add(req) { + Some(v) => v, + None => return null_mut(), + }; + let align_req = 16; + let needed = align_up(needed_unaligned, align_req); + + let mut allocator = ALLOCATOR.lock(); + + if let Some(region) = allocator.find_region(needed) { + unsafe { + let header_ptr = region.start as *mut Header; + header_ptr.write(Header { + magic: HEADER_MAGIC_ALLOC, + _pad: 0, + alloc_size: (region.end - region.start).saturating_sub(hdr), + region_size: region.end - region.start, + }); + return (region.start + hdr) as *mut u8; + } + } + + let min_region = mem::size_of::(); + let req_region = core::cmp::max(needed, min_region); + + let align = align_req; + let over = match req_region.checked_add(align) { + Some(v) => v, + None => return null_mut(), + }; + if over > i64::MAX as usize { + return null_mut(); + } + + let raw_start = sbrk(over as i64); + if raw_start == -1 { + return null_mut(); + } + + let raw_start = raw_start as usize; + let aligned_start = align_up(raw_start, align); + let usable = over - (aligned_start - raw_start); + + if usable < min_region { + return null_mut(); + } + + unsafe { + allocator.add_free_memory_region(aligned_start, usable); + } + + drop(allocator); + malloc(size) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn realloc(ptr: *mut u8, size: usize) -> *mut u8 { + if size == 0 { + free(ptr); + return null_mut(); + } + + if ptr.is_null() { + return malloc(size as u64); + } + + unsafe { + let hdr = mem::size_of::
(); + let header_ptr = (ptr as usize - hdr) as *mut Header; + + if (*header_ptr).magic != HEADER_MAGIC_ALLOC { + return null_mut(); + } + + let old_alloc_size = (*header_ptr).alloc_size; + + if size <= old_alloc_size { + return ptr; + } + + let new_ptr = malloc(size as u64); + if new_ptr.is_null() { + return null_mut(); + } + + core::ptr::copy_nonoverlapping(ptr, new_ptr, old_alloc_size); + free(ptr); + new_ptr + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn memset(dest: *mut u8, c: c_int, n: usize) -> *mut u8 { + if n == 0 { + return dest; + } + if dest.is_null() { + return null_mut(); + } + + let byte = c as u8; + let mut i = 0usize; + while i < n { + unsafe { core::ptr::write_volatile(dest.add(i), byte) }; + i += 1; + } + + dest +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 { + if n == 0 { + return dest; + } + if dest.is_null() || src.is_null() { + return null_mut(); + } + + let mut i = 0usize; + while i < n { + let v = unsafe { core::ptr::read_volatile(src.add(i)) }; + unsafe { core::ptr::write_volatile(dest.add(i), v) }; + i += 1; + } + + dest +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn memmove(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 { + if n == 0 { + return dest; + } + if dest.is_null() || src.is_null() { + return null_mut(); + } + + let dest_addr = dest as usize; + let src_addr = src as usize; + + if dest_addr == src_addr { + return dest; + } + + if dest_addr < src_addr || dest_addr >= src_addr.saturating_add(n) { + let mut i = 0usize; + while i < n { + let v = unsafe { core::ptr::read_volatile(src.add(i)) }; + unsafe { core::ptr::write_volatile(dest.add(i), v) }; + i += 1; + } + } else { + let mut i = n; + while i != 0 { + i -= 1; + let v = unsafe { core::ptr::read_volatile(src.add(i)) }; + unsafe { core::ptr::write_volatile(dest.add(i), v) }; + } + } + + dest +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn memcmp(a: *const u8, b: *const u8, n: usize) -> i32 { + if n == 0 { + return 0; + } + if a.is_null() || b.is_null() { + return 0; + } + + let mut i = 0usize; + while i < n { + let av = unsafe { core::ptr::read_volatile(a.add(i)) }; + let bv = unsafe { core::ptr::read_volatile(b.add(i)) }; + if av != bv { + return av as i32 - bv as i32; + } + i += 1; + } + 0 +} diff --git a/src/syscall.rs b/src/syscall.rs new file mode 100644 index 0000000..8391b14 --- /dev/null +++ b/src/syscall.rs @@ -0,0 +1,77 @@ +pub const READ: usize = 0; +pub const WRITE: usize = 1; +pub const OPEN: usize = 2; +pub const CLOSE: usize = 3; +pub const STAT: usize = 4; +pub const LSEEK: usize = 8; +pub const MMAP: usize = 9; +pub const MUNMAP: usize = 9; +pub const BRK: usize = 12; +pub const GETPID: usize = 39; +pub const FORK: usize = 57; +pub const EXECVE: usize = 59; +pub const EXIT: usize = 60; +pub const WAIT4: usize = 61; +pub const KILL: usize = 62; +pub const CHDIR: usize = 80; +pub const MKDIR: usize = 83; +pub const UNLINK: usize = 87; +pub const GETDENTS64: usize = 217; +pub const CLOCK_GETTIME: usize = 228; +pub const EXIT_GROUP: usize = 231; +pub const SLEEP: usize = 909090; // zzz haha +pub const DRAW_PIXEL: usize = 5555; +pub const DRAW_BUFFER: usize = 7777; +pub const FRAMEBUFFER_SWAP: usize = 6666; + +#[inline(always)] +pub unsafe fn syscall0(num: usize) -> isize { + let ret: isize; + unsafe { + core::arch::asm!( + "int 0x80", + in("rax") num, + lateout("rax") ret, + clobber_abi("sysv64"), + options(nostack) + ); + } + + ret +} + +#[inline(always)] +pub unsafe fn syscall1(num: usize, arg0: isize) -> isize { + let ret: isize; + unsafe { + core::arch::asm!( + "int 0x80", + in("rax") num, + in("rdi") arg0, + lateout("rax") ret, + clobber_abi("sysv64"), + options(nostack) + ); + } + + ret +} + +#[inline(always)] +pub unsafe fn syscall3(num: usize, arg0: isize, arg1: isize, arg2: isize) -> isize { + let ret: isize; + unsafe { + core::arch::asm!( + "int 0x80", + in("rax") num, + in("rdi") arg0, + in("rsi") arg1, + in("rdx") arg2, + lateout("rax") ret, + clobber_abi("sysv64"), + options(nostack) + ); + } + + ret +} diff --git a/src/time.rs b/src/time.rs new file mode 100644 index 0000000..48d36c4 --- /dev/null +++ b/src/time.rs @@ -0,0 +1,38 @@ +use crate::syscall::{CLOCK_GETTIME, SLEEP, syscall0, syscall1}; + +#[repr(C)] +pub struct Timeval { + pub tv_sec: i64, + pub tv_usec: i64, +} + +#[repr(C)] +pub struct Timezone { + pub tz_minuteswest: i32, + pub tz_dsttime: i32, +} + +pub const TICKS_PER_SECOND: u64 = 1000; + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sleep_ms(ms: i32) -> i32 { + unsafe { syscall1(SLEEP, ms as isize) }; + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn gettimeofday(tv: *mut Timeval, _tz: *mut Timezone) -> i32 { + unsafe { + if !tv.is_null() { + let ticks = syscall0(CLOCK_GETTIME) as u64; + + let seconds = ticks / TICKS_PER_SECOND; + let microseconds = (ticks % TICKS_PER_SECOND) * (1_000_000 / TICKS_PER_SECOND); + + (*tv).tv_sec = seconds as i64; + (*tv).tv_usec = microseconds as i64; + } + } + + 0 +} diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..dc50b4d --- /dev/null +++ b/src/util.rs @@ -0,0 +1,20 @@ +#[inline] +pub const fn align_down(addr: usize, align: usize) -> usize { + assert!(align.is_power_of_two(), "`align` must be a power of two"); + addr & !(align - 1) +} + +#[inline] +pub const fn align_up(addr: usize, align: usize) -> usize { + assert!(align.is_power_of_two(), "`align` must be a power of two"); + let align_mask = align - 1; + if addr & align_mask == 0 { + addr + } else { + if let Some(aligned) = (addr | align_mask).checked_add(1) { + aligned + } else { + panic!("attempt to add with overflow") + } + } +}