diff --git a/hello.c b/hello.c index ffada6b..d61a13f 100644 --- a/hello.c +++ b/hello.c @@ -1,5 +1,4 @@ #include - int main(int argc, char *argv[]) { int result = printf("Hello, world!\n"); if (result != 15) { diff --git a/libuwuc/src/error.rs b/libuwuc/src/error.rs new file mode 100644 index 0000000..8ef22f1 --- /dev/null +++ b/libuwuc/src/error.rs @@ -0,0 +1,51 @@ +use core::{cell::UnsafeCell, ptr::addr_of}; + +#[thread_local] +static ERRNO: UnsafeCell = UnsafeCell::new(0); + +pub fn errno_location() -> *const i32 { + addr_of!(ERRNO).cast() +} + +pub fn errno() -> i32 { + unsafe { *ERRNO.get() } +} + +pub fn set_errno(errno: i32) { + unsafe { ERRNO.get().write(errno) } +} + +pub const EPERM: i32 = 1; /* Operation not permitted */ +pub const ENOENT: i32 = 2; /* No such file or directory */ +pub const ESRCH: i32 = 3; /* No such process */ +pub const EINTR: i32 = 4; /* Interrupted system call */ +pub const EIO: i32 = 5; /* I/O error */ +pub const ENXIO: i32 = 6; /* No such device or address */ +pub const E2BIG: i32 = 7; /* Argument list too long */ +pub const ENOEXEC: i32 = 8; /* Exec format error */ +pub const EBADF: i32 = 9; /* Bad file number */ +pub const ECHILD: i32 = 10; /* No child processes */ +pub const EAGAIN: i32 = 11; /* Try again */ +pub const ENOMEM: i32 = 12; /* Out of memory */ +pub const EACCES: i32 = 13; /* Permission denied */ +pub const EFAULT: i32 = 14; /* Bad address */ +pub const ENOTBLK: i32 = 15; /* Block device required */ +pub const EBUSY: i32 = 16; /* Device or resource busy */ +pub const EEXIST: i32 = 17; /* File exists */ +pub const EXDEV: i32 = 18; /* Cross-device link */ +pub const ENODEV: i32 = 19; /* No such device */ +pub const ENOTDIR: i32 = 20; /* Not a directory */ +pub const EISDIR: i32 = 21; /* Is a directory */ +pub const EINVAL: i32 = 22; /* Invalid argument */ +pub const ENFILE: i32 = 23; /* File table overflow */ +pub const EMFILE: i32 = 24; /* Too many open files */ +pub const ENOTTY: i32 = 25; /* Not a typewriter */ +pub const ETXTBSY: i32 = 26; /* Text file busy */ +pub const EFBIG: i32 = 27; /* File too large */ +pub const ENOSPC: i32 = 28; /* No space left on device */ +pub const ESPIPE: i32 = 29; /* Illegal seek */ +pub const EROFS: i32 = 30; /* Read-only file system */ +pub const EMLINK: i32 = 31; /* Too many links */ +pub const EPIPE: i32 = 32; /* Broken pipe */ +pub const EDOM: i32 = 33; /* Math argument out of domain of func */ +pub const ERANGE: i32 = 34; /* Math result not representable */ diff --git a/libuwuc/src/fmt/mod.rs b/libuwuc/src/fmt/mod.rs index 9c26847..1ca15c7 100644 --- a/libuwuc/src/fmt/mod.rs +++ b/libuwuc/src/fmt/mod.rs @@ -3,9 +3,9 @@ use core::ffi::c_int; pub mod parse; pub mod printf; -pub fn is_space(c: c_int) -> c_int { +pub fn is_space(c: c_int) -> bool { // todo: is this correct? char::from_u32(c as _) .map(char::is_whitespace) - .unwrap_or_default() as _ + .unwrap_or_default() } diff --git a/libuwuc/src/fmt/parse.rs b/libuwuc/src/fmt/parse.rs index 5e44722..8d44628 100644 --- a/libuwuc/src/fmt/parse.rs +++ b/libuwuc/src/fmt/parse.rs @@ -1,18 +1,161 @@ use core::ffi::{c_int, c_long}; -use crate::utils::SharedThinCstr; +use crate::{ + error::{set_errno, EINVAL}, + utils::SharedThinCstr, +}; -pub unsafe fn parse_long<'a>( +pub fn parse_long<'a>( str: SharedThinCstr<'a>, - endptr: *mut SharedThinCstr<'a>, + endptr: Option<&mut Option>>, base: c_int, ) -> c_long { - let mut cur = str; - while cur - .first() - .map(|c| super::is_space(c as _) == 1) - .unwrap_or(false) - {} + if base != 10 { + todo!(); + } + let mut chars = str.into_iter().enumerate().peekable(); - 0 + while chars + .peek() + .map(|&(_, c)| super::is_space(c as _)) + .unwrap_or(false) + { + chars.next(); + } + + let mut negate = false; + let mut acc: i64 = 0; + + let write_end = |pos: usize| { + if let Some(endptr) = endptr { + *endptr = Some(unsafe { str.add(pos) }); + } + }; + + let mut last_pos; + + let c = chars.peek(); + match c { + Some((pos, b'+')) => { + last_pos = *pos; + chars.next(); + } + Some((pos, b'-')) => { + last_pos = *pos; + chars.next(); + negate = true + } + Some((_, c)) if !c.is_ascii_digit() => { + write_end(0); + set_errno(EINVAL); + return 0; + } + None => { + write_end(0); + set_errno(EINVAL); + return 0; + } + Some((pos, _)) => { + last_pos = *pos; + } + } + + loop { + match chars.next() { + Some((pos, c)) if c.is_ascii_digit() => { + last_pos = pos; + let n = c - b'0'; + acc *= 10; + if negate { + acc -= n as i64; + } else { + acc += n as i64; + } + } + Some((pos, _)) => { + write_end(pos); + break; + } + None => { + write_end(last_pos + 1); + break; + } + } + } + + acc +} + +#[cfg(test)] +mod tests { + use crate::{cstr, utils::SharedThinCstr}; + + fn test_strtol(str: SharedThinCstr<'_>, expected: i64, base: i32, parsed_len: usize) { + let mut end = None; + let result = super::parse_long(str, Some(&mut end), base); + assert_eq!(result, expected); + let end = end.unwrap(); + let read = end.as_raw() as usize - str.as_raw() as usize; + assert_eq!(read, parsed_len); + } + + #[test] + fn l_zero() { + test_strtol(cstr!("0"), 0, 10, 1); + } + + #[test] + fn l_ten() { + test_strtol(cstr!("10"), 10, 10, 2); + } + + #[test] + fn l_eleven() { + test_strtol(cstr!("11"), 11, 10, 2); + } + + #[test] + fn negative_zero() { + test_strtol(cstr!("-0"), 0, 10, 2); + } + + #[test] + fn negative_ten() { + test_strtol(cstr!("-10"), -10, 10, 3); + } + + #[test] + fn negative_eleven() { + test_strtol(cstr!("-11"), -11, 10, 3); + } + + #[test] + fn l_leading_whitespace() { + test_strtol(cstr!("\t 1"), 1, 10, 3); + } + + #[test] + fn l_trailing_garbage_one() { + test_strtol(cstr!("3uwu"), 3, 10, 1); + } + + #[test] + fn l_trailing_garbage_two() { + test_strtol(cstr!("32uwu"), 32, 10, 2); + } + + #[test] + fn l_trailing_garbage_many() { + test_strtol(cstr!("12345uwu"), 12345, 10, 5); + } + + #[test] + fn l_long_max() { + test_strtol(cstr!("9223372036854775807"), i64::MAX, 10, 19); + } + + #[test] + fn l_long_min() { + test_strtol(cstr!("-9223372036854775808"), i64::MIN, 10, 20); + } } diff --git a/libuwuc/src/lib.rs b/libuwuc/src/lib.rs index 6bec230..4ea625a 100644 --- a/libuwuc/src/lib.rs +++ b/libuwuc/src/lib.rs @@ -1,5 +1,7 @@ #![no_std] #![feature(c_variadic)] +#![feature(thread_local)] + #![warn(unreachable_pub)] #![warn(rust_2018_idioms)] #![allow(clippy::missing_safety_doc)] @@ -9,6 +11,7 @@ extern crate std; pub mod alloc; pub mod env; +pub mod error; pub mod fmt; pub mod io; pub mod mem; diff --git a/libuwuc/src/sys/x86_64/syscall/mod.rs b/libuwuc/src/sys/x86_64/syscall/mod.rs index f990ca5..b327a05 100644 --- a/libuwuc/src/sys/x86_64/syscall/mod.rs +++ b/libuwuc/src/sys/x86_64/syscall/mod.rs @@ -12,6 +12,9 @@ macro_rules! syscall { "syscall", in("rdi") $number, lateout("rax") out, + // rcx and r11 are clobbered https://github.com/torvalds/linux/blob/3b517966c5616ac011081153482a5ba0e91b17ff/tools/include/nolibc/arch-x86_64.h#L19 + out("rcx") _, + out("r11") _, ); out }}; @@ -22,6 +25,9 @@ macro_rules! syscall { in("rax") $number, in("rdi") $arg1, lateout("rax") out, + // rcx and r11 are clobbered + out("rcx") _, + out("r11") _, ); out }}; @@ -33,6 +39,9 @@ macro_rules! syscall { in("rdi") $arg1, in("rsi") $arg2, lateout("rax") out, + // rcx and r11 are clobbered + out("rcx") _, + out("r11") _, ); out }}; @@ -45,6 +54,9 @@ macro_rules! syscall { in("rsi") $arg2, in("rdx") $arg3, lateout("rax") out, + // rcx and r11 are clobbered + out("rcx") _, + out("r11") _, ); out }}; @@ -58,6 +70,9 @@ macro_rules! syscall { in("rdx") $arg3, in("r10") $arg4, lateout("rax") out, + // rcx and r11 are clobbered + out("rcx") _, + out("r11") _, ); out }}; @@ -72,6 +87,9 @@ macro_rules! syscall { in("r10") $arg4, in("r8") $arg5, lateout("rax") out, + // rcx and r11 are clobbered + out("rcx") _, + out("r11") _, ); out }}; @@ -87,6 +105,9 @@ macro_rules! syscall { in("r8") $arg5, in("r9") $arg6, lateout("rax") out, + // rcx and r11 are clobbered + out("rcx") _, + out("r11") _, ); out }}; diff --git a/rawc/src/rt.rs b/rawc/src/rt.rs index 964be24..28fbffa 100644 --- a/rawc/src/rt.rs +++ b/rawc/src/rt.rs @@ -5,3 +5,8 @@ pub extern "C" fn __stack_chk_fail() -> ! { libuwuc::start::abort(); } } + +#[no_mangle] +pub extern "C" fn __errno_location() -> *const i32 { + libuwuc::error::errno_location() +} \ No newline at end of file diff --git a/rawc/src/stdlib.rs b/rawc/src/stdlib.rs index 2a72434..5a5e07e 100644 --- a/rawc/src/stdlib.rs +++ b/rawc/src/stdlib.rs @@ -1,3 +1,5 @@ +use core::ffi::{c_int, c_long}; + use libuwuc::utils::SharedThinCstr; #[no_mangle] @@ -15,6 +17,21 @@ pub unsafe extern "C" fn exit(code: i32) -> ! { libuwuc::start::exit(code as i64 as _) } +#[no_mangle] +pub unsafe extern "C" fn strtol(nptr: *const u8, endptr: *mut *const u8, base: c_int) -> c_long { + let str = SharedThinCstr::from_raw(nptr); + libuwuc::fmt::parse::parse_long( + str, + core::mem::transmute::<*mut *const u8, Option<&mut Option>>>(endptr), + base, + ) +} + +#[no_mangle] +pub unsafe extern "C" fn strtoll(nptr: *const u8, endptr: *mut *const u8, base: c_int) -> c_long { + strtol(nptr, endptr, base) +} + #[no_mangle] pub unsafe extern "C" fn getenv(name: *const u8) -> *const u8 { libuwuc::env::getenv(SharedThinCstr::from_raw(name))