From 312deb8a5611a9653df769c5dcc7f6c4b028cf39 Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Tue, 12 Nov 2024 17:07:06 +0200 Subject: [PATCH] libc: dynamic libc --- kernel/libk/src/task/binary/elf.rs | 3 +- kernel/libk/src/task/binary/mod.rs | 1 + test.c | 8 +- userspace/dyn-loader/src/main.rs | 2 + userspace/dyn-loader/src/object.rs | 16 +- userspace/dyn-loader/src/relocation.rs | 3 + userspace/dyn-loader/src/state.rs | 3 +- userspace/lib/ygglibc/Cargo.lock | 25 ++ userspace/lib/ygglibc/Cargo.toml | 4 +- userspace/lib/ygglibc/build.rs | 33 +- userspace/lib/ygglibc/crt/x86_64/crt0.S | 11 + .../lib/ygglibc/etc/x86_64-unknown-none.json | 13 +- userspace/lib/ygglibc/include/bits/sys/stat.h | 16 + userspace/lib/ygglibc/include/bits/time.h | 6 + userspace/lib/ygglibc/src/error.rs | 13 + userspace/lib/ygglibc/src/headers/mod.rs | 13 +- .../lib/ygglibc/src/headers/stdlib/conv.rs | 416 ++++++++++++++++++ .../lib/ygglibc/src/headers/stdlib/io.rs | 8 + .../lib/ygglibc/src/headers/stdlib/malloc.rs | 47 ++ .../lib/ygglibc/src/headers/stdlib/math.rs | 61 +++ .../lib/ygglibc/src/headers/stdlib/mod.rs | 32 +- .../lib/ygglibc/src/headers/stdlib/process.rs | 61 +++ .../lib/ygglibc/src/headers/stdlib/random.rs | 17 + .../lib/ygglibc/src/headers/stdlib/util.rs | 134 ++++++ .../lib/ygglibc/src/headers/stdlib/wide.rs | 6 + .../src/headers/sys_stat/cbindgen.toml | 19 + .../lib/ygglibc/src/headers/sys_stat/mod.rs | 221 ++++++++++ .../src/headers/sys_time/cbindgen.toml | 14 + .../lib/ygglibc/src/headers/sys_time/mod.rs | 20 + .../lib/ygglibc/src/headers/sys_types/mod.rs | 4 + .../ygglibc/src/headers/time/cbindgen.toml | 20 + .../lib/ygglibc/src/headers/time/convert.rs | 64 +++ userspace/lib/ygglibc/src/headers/time/mod.rs | 113 +++++ .../lib/ygglibc/src/headers/time/string.rs | 112 +++++ .../lib/ygglibc/src/headers/time/timer.rs | 55 +++ .../lib/ygglibc/src/headers/time/util.rs | 39 ++ userspace/lib/ygglibc/src/lib.rs | 36 +- userspace/lib/ygglibc/src/panic.rs | 8 + userspace/lib/ygglibc/src/process.rs | 6 +- userspace/lib/ygglibc/src/ssp.rs | 15 + userspace/lib/ygglibc/src/util.rs | 93 +++- xtask/src/build/cargo.rs | 8 +- xtask/src/build/userspace.rs | 62 ++- 43 files changed, 1790 insertions(+), 71 deletions(-) create mode 100644 userspace/lib/ygglibc/crt/x86_64/crt0.S create mode 100644 userspace/lib/ygglibc/include/bits/sys/stat.h create mode 100644 userspace/lib/ygglibc/include/bits/time.h create mode 100644 userspace/lib/ygglibc/src/headers/stdlib/conv.rs create mode 100644 userspace/lib/ygglibc/src/headers/stdlib/io.rs create mode 100644 userspace/lib/ygglibc/src/headers/stdlib/malloc.rs create mode 100644 userspace/lib/ygglibc/src/headers/stdlib/math.rs create mode 100644 userspace/lib/ygglibc/src/headers/stdlib/process.rs create mode 100644 userspace/lib/ygglibc/src/headers/stdlib/random.rs create mode 100644 userspace/lib/ygglibc/src/headers/stdlib/util.rs create mode 100644 userspace/lib/ygglibc/src/headers/stdlib/wide.rs create mode 100644 userspace/lib/ygglibc/src/headers/sys_stat/cbindgen.toml create mode 100644 userspace/lib/ygglibc/src/headers/sys_stat/mod.rs create mode 100644 userspace/lib/ygglibc/src/headers/sys_time/cbindgen.toml create mode 100644 userspace/lib/ygglibc/src/headers/sys_time/mod.rs create mode 100644 userspace/lib/ygglibc/src/headers/time/cbindgen.toml create mode 100644 userspace/lib/ygglibc/src/headers/time/convert.rs create mode 100644 userspace/lib/ygglibc/src/headers/time/mod.rs create mode 100644 userspace/lib/ygglibc/src/headers/time/string.rs create mode 100644 userspace/lib/ygglibc/src/headers/time/timer.rs create mode 100644 userspace/lib/ygglibc/src/headers/time/util.rs create mode 100644 userspace/lib/ygglibc/src/panic.rs create mode 100644 userspace/lib/ygglibc/src/ssp.rs diff --git a/kernel/libk/src/task/binary/elf.rs b/kernel/libk/src/task/binary/elf.rs index 4dc684fc..b4393027 100644 --- a/kernel/libk/src/task/binary/elf.rs +++ b/kernel/libk/src/task/binary/elf.rs @@ -289,13 +289,14 @@ pub fn clone_tls(space: &ProcessAddressSpace, image: &ProcessImage) -> Result( elf: &mut ElfStream>, ) -> Result { - if elf.ehdr.e_type != elf::abi::ET_DYN { + if elf.ehdr.e_type != elf::abi::ET_DYN && elf.ehdr.e_type != elf::abi::ET_EXEC { return Ok(false); } let dynamic = elf.dynamic().map_err(from_parse_error)?; let Some(dynamic) = dynamic else { // No .dynamic section -> not dynamic + log::debug!("No .dynamic -> not dynamic"); return Ok(false); }; diff --git a/kernel/libk/src/task/binary/mod.rs b/kernel/libk/src/task/binary/mod.rs index 0a830c07..c67f1000 100644 --- a/kernel/libk/src/task/binary/mod.rs +++ b/kernel/libk/src/task/binary/mod.rs @@ -248,6 +248,7 @@ fn xxx_load_program>( file.seek(SeekFrom::Start(0))?; + log::debug!("open_elf_file({:?})", path); if head.starts_with(b"\x7FELF") { match elf::open_elf_file(file)? { ElfKind::Static(elf, file) => { diff --git a/test.c b/test.c index b1c3bd8c..e1f92417 100644 --- a/test.c +++ b/test.c @@ -2,8 +2,12 @@ #include #include #include +#include +#include + +int main(int argc, const char **argv) { + int x = 1234; + printf("Hello %d!!!\n", x); -int main() { - printf("Hello!\n"); return 0; } diff --git a/userspace/dyn-loader/src/main.rs b/userspace/dyn-loader/src/main.rs index c649ff4a..29169bdb 100644 --- a/userspace/dyn-loader/src/main.rs +++ b/userspace/dyn-loader/src/main.rs @@ -94,6 +94,8 @@ fn run>(path: P, args: &[String]) -> Result<(), Error> { // Then relocate the main object main_object.relocate(&mut state)?; + debug_trace!("Load finished"); + if !state.undefined_references.is_empty() { for item in state.undefined_references.iter() { eprintln!("Undefined reference to {:?}", item); diff --git a/userspace/dyn-loader/src/object.rs b/userspace/dyn-loader/src/object.rs index be7fafe9..92cb7935 100644 --- a/userspace/dyn-loader/src/object.rs +++ b/userspace/dyn-loader/src/object.rs @@ -56,6 +56,7 @@ impl ObjectTls { impl Object { pub fn open>(path: P) -> Result { let path = path.as_ref().to_owned(); + debug_trace!("Object::open({:?})", path); let file = BufReader::new(File::open(&path)?); let mut elf = ElfStream::open_stream(file)?; let file = BufReader::new(File::open(&path)?); @@ -123,6 +124,7 @@ impl Object { return Ok(None); } let vma = self.elf.ehdr.e_entry as usize; + debug_trace!("entry = {:#x}", mapping.base + vma - self.vma_start); Ok(Some(unsafe { std::mem::transmute(mapping.base + vma - self.vma_start) @@ -138,6 +140,7 @@ impl Object { let size = self.vma_end - self.vma_start; let mut mapping = ObjectMapping::new(size)?; + let base = mapping.base; let object_data = mapping.as_slice_mut(); for segment in self.elf.segments() { @@ -154,6 +157,13 @@ impl Object { elf::abi::PT_LOAD => { let rel_offset = segment.p_vaddr as usize - self.vma_start; + debug_trace!( + "{:?} {:#x?} -> {:#x?}", + self.path, + segment.p_vaddr..segment.p_vaddr + mem_size as u64, + base + rel_offset..base + rel_offset + mem_size + ); + let segment_data = &mut object_data[rel_offset..rel_offset + mem_size]; if file_size > 0 { @@ -189,7 +199,7 @@ impl Object { continue; } - state.export_symbol(dynsym, mapping.base, &self.path, self.tls_module_id); + state.export_symbol(dynsym, mapping.base, &self.path, self.tls_module_id, self.vma_start); } self.mapping = Some(mapping); @@ -201,6 +211,7 @@ impl Object { for dynsym in self.dynamic_symbol_array.iter_mut() { if dynsym.value.is_none() { + debug_trace!("Resolve {:?}", dynsym.name); state.resolve_symbol(dynsym); } else if dynsym.raw.st_shndx == elf::abi::SHN_UNDEF { state.undefined_references.push(dynsym.name.clone()); @@ -227,8 +238,9 @@ impl Object { self.tls_module_id, |idx| Ok(&self.dynamic_symbol_array[idx as usize]), mapping.base, + self.vma_start )? { - value.write(mapping, entry.r_offset); + value.write(mapping, entry.r_offset - self.vma_start as u64); } } } diff --git a/userspace/dyn-loader/src/relocation.rs b/userspace/dyn-loader/src/relocation.rs index bb8aba24..014d06ee 100644 --- a/userspace/dyn-loader/src/relocation.rs +++ b/userspace/dyn-loader/src/relocation.rs @@ -35,6 +35,7 @@ pub trait RelocationExt { tls_module_id: Option, image_symbol: F, image_base: usize, + vma_start: usize, ) -> Result, Error>; } @@ -46,6 +47,7 @@ impl RelocationExt for Rela { tls_module_id: Option, image_symbol: F, image_base: usize, + vma_start: usize, ) -> Result, Error> { let image_base = image_base as i64; let symbol = image_symbol(self.r_sym)?; @@ -114,6 +116,7 @@ impl RelocationExt for Rela { tls_module_id: Option, image_symbol: F, image_base: usize, + vma_start: usize, ) -> Result, Error> { let image_base = image_base as i64; let symbol = image_symbol(self.r_sym)?; diff --git a/userspace/dyn-loader/src/state.rs b/userspace/dyn-loader/src/state.rs index c8c8e8e6..e3503f62 100644 --- a/userspace/dyn-loader/src/state.rs +++ b/userspace/dyn-loader/src/state.rs @@ -51,6 +51,7 @@ impl State { load_base: usize, source: P, tls_index: Option, + vma_start: usize, ) { if sym.raw.st_symtype() == elf::abi::STT_TLS { // If it exports TLS symbols, it has TLS, I guess @@ -72,7 +73,7 @@ impl State { sym.value = Some(value); sym.tls_index = Some(module_id); } else { - let sym_value = sym.raw.st_value as usize + load_base; + let sym_value = sym.raw.st_value as usize + load_base - vma_start; let value = if let Some((value, _)) = self.symbol_table.get(&sym.name) { *value } else { diff --git a/userspace/lib/ygglibc/Cargo.lock b/userspace/lib/ygglibc/Cargo.lock index 90c5a348..f7ba7095 100644 --- a/userspace/lib/ygglibc/Cargo.lock +++ b/userspace/lib/ygglibc/Cargo.lock @@ -65,6 +65,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + [[package]] name = "bitflags" version = "2.6.0" @@ -124,6 +130,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "num-traits", +] + [[package]] name = "clap" version = "4.5.20" @@ -245,6 +260,15 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.20.2" @@ -570,6 +594,7 @@ version = "0.1.0" dependencies = [ "bitflags", "cbindgen", + "chrono", "libyalloc", "yggdrasil-abi", "yggdrasil-rt", diff --git a/userspace/lib/ygglibc/Cargo.toml b/userspace/lib/ygglibc/Cargo.toml index bce22679..14ff47a9 100644 --- a/userspace/lib/ygglibc/Cargo.toml +++ b/userspace/lib/ygglibc/Cargo.toml @@ -4,13 +4,15 @@ version = "0.1.0" edition = "2021" [lib] -crate-type = ["staticlib"] +crate-type = ["cdylib", "staticlib"] [dependencies] yggdrasil-rt = { path = "../../../lib/runtime" } yggdrasil-abi = { path = "../../../lib/abi", features = ["alloc", "bytemuck"] } libyalloc = { path = "../../../lib/libyalloc" } + bitflags = "2.6.0" +chrono = { version = "0.4.31", default-features = false } [build-dependencies] cbindgen = { git = "https://git.alnyan.me/yggdrasil/cbindgen.git", branch = "master" } diff --git a/userspace/lib/ygglibc/build.rs b/userspace/lib/ygglibc/build.rs index 5f44d49b..8ced7e8e 100644 --- a/userspace/lib/ygglibc/build.rs +++ b/userspace/lib/ygglibc/build.rs @@ -1,5 +1,8 @@ use std::{ - env, fs::{self, DirEntry}, path::Path + env, + fs::{self, DirEntry}, + path::{Path, PathBuf}, + process::Command, }; const RENAMES: &[(&str, &str)] = &[ @@ -32,9 +35,7 @@ fn generate_header(config_path: impl AsRef, header_output: impl AsRef, header_output: impl AsRef) { + let output_dir = output_dir.as_ref(); + let mut command = Command::new("clang"); + command + .arg(format!("--target={}-unknown-none", arch)) + .arg("-nostdlib") + .arg("-nostdinc") + .arg("-c") + .arg("-o") + .arg(output_dir.join("crt0.o")) + .arg(format!("crt/{}/crt0.S", arch)); + + if !command.status().unwrap().success() { + panic!("Couldn't compile crt0.S"); + } +} + fn main() { let target = env::var("TARGET").expect("$TARGET is not set"); let profile = env::var("PROFILE").expect("$PROFILE is not set"); + let arch = env::var("CARGO_CFG_TARGET_ARCH").expect("$CARGO_CFG_TARGET_ARCH is not set"); - let header_output = format!("target/{target}/{profile}/include"); + let output_dir = PathBuf::from(format!("target/{target}/{profile}")); + let header_output = output_dir.join("include"); + + compile_crt0(&arch, output_dir); fs::read_dir("src/headers") .unwrap() @@ -72,4 +94,3 @@ fn main() { generate_header(&p, &header_output); }); } - diff --git a/userspace/lib/ygglibc/crt/x86_64/crt0.S b/userspace/lib/ygglibc/crt/x86_64/crt0.S new file mode 100644 index 00000000..3129f7ac --- /dev/null +++ b/userspace/lib/ygglibc/crt/x86_64/crt0.S @@ -0,0 +1,11 @@ +.section .text + +.global _start +.extern __ygglibc_entry + +.type _start, %function +_start: + // %rdi -- kernel argument + leaq __ygglibc_entry(%rip), %rax + jmp *%rax +.size _start, . - _start diff --git a/userspace/lib/ygglibc/etc/x86_64-unknown-none.json b/userspace/lib/ygglibc/etc/x86_64-unknown-none.json index 7a318b01..d79fc1f4 100644 --- a/userspace/lib/ygglibc/etc/x86_64-unknown-none.json +++ b/userspace/lib/ygglibc/etc/x86_64-unknown-none.json @@ -13,11 +13,18 @@ "executables": true, "panic-strategy": "abort", "dynamic-linking": true, - "relocation-model": "static", - "position-independent-executables": false, + "relocation-model": "pic", + "position-independent-executables": true, + "crt-static-allows-dylibs": true, "has-thread-local": false, "linker": "rust-lld", - "linker-flavor": "ld.lld" + "linker-flavor": "ld.lld", + + "late-link-args-dynamic": { + "ld.lld": [ + "--dynamic-linker=/libexec/dyn-loader" + ] + } } diff --git a/userspace/lib/ygglibc/include/bits/sys/stat.h b/userspace/lib/ygglibc/include/bits/sys/stat.h new file mode 100644 index 00000000..42a185a4 --- /dev/null +++ b/userspace/lib/ygglibc/include/bits/sys/stat.h @@ -0,0 +1,16 @@ +#ifndef _YGGDRASIL_SYS_STAT_H +#define _YGGDRASIL_SYS_STAT_H 1 + +#define st_atime st_atim.tv_sec +#define st_mtime st_mtim.tv_sec +#define st_ctime st_ctim.tv_sec + +#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) + +#endif diff --git a/userspace/lib/ygglibc/include/bits/time.h b/userspace/lib/ygglibc/include/bits/time.h new file mode 100644 index 00000000..5f7034fd --- /dev/null +++ b/userspace/lib/ygglibc/include/bits/time.h @@ -0,0 +1,6 @@ +#ifndef _YGGDRASIL_TIME_H +#define _YGGDRASIL_TIME_H 1 + +typedef struct timespec __ygg_timespec_t; + +#endif diff --git a/userspace/lib/ygglibc/src/error.rs b/userspace/lib/ygglibc/src/error.rs index 10c681ac..3ea73649 100644 --- a/userspace/lib/ygglibc/src/error.rs +++ b/userspace/lib/ygglibc/src/error.rs @@ -31,6 +31,10 @@ pub trait ResultExt { fn e_map_err Errno>(self, map: F) -> EResult; } +pub trait OptionExt { + fn e_ok_or(self, err: Errno) -> EResult; +} + pub trait CResult { const ERROR: Self; } @@ -144,6 +148,15 @@ impl ResultExt for Result { } } +impl OptionExt for Option { + fn e_ok_or(self, err: Errno) -> EResult { + match self { + Some(value) => EResult::Ok(value), + None => EResult::Err(err) + } + } +} + impl Try for EResult { type Output = T; type Residual = EResult; diff --git a/userspace/lib/ygglibc/src/headers/mod.rs b/userspace/lib/ygglibc/src/headers/mod.rs index cb7e501b..2f9d1395 100644 --- a/userspace/lib/ygglibc/src/headers/mod.rs +++ b/userspace/lib/ygglibc/src/headers/mod.rs @@ -1,4 +1,4 @@ -#![allow(non_camel_case_types)] +#![allow(non_camel_case_types, non_upper_case_globals)] // - // - @@ -60,11 +60,11 @@ // - // - // - -// - +// = // - -// - +// = // - -// - +// + // - // - // - @@ -73,7 +73,7 @@ // - // - // - -// - +// = // - // - // ~ @@ -96,5 +96,8 @@ pub mod string; pub mod strings; pub mod unistd; pub mod signal; +pub mod time; pub mod sys_types; +pub mod sys_stat; +pub mod sys_time; diff --git a/userspace/lib/ygglibc/src/headers/stdlib/conv.rs b/userspace/lib/ygglibc/src/headers/stdlib/conv.rs new file mode 100644 index 00000000..a7fa3043 --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/stdlib/conv.rs @@ -0,0 +1,416 @@ +use core::{ + ffi::{c_char, c_double, c_float, c_int, c_long, c_longlong, c_ulong, c_ulonglong}, + ptr::{null_mut, NonNull}, +}; + +use crate::util::cstr_matches_insensitive; + +// base64 + +#[no_mangle] +unsafe extern "C" fn a64l(_s: *const c_char) -> c_long { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn l64a(_v: c_long) -> *mut c_char { + todo!() +} + +// floats + +#[no_mangle] +unsafe extern "C" fn atof(s: *const c_char) -> c_double { + strtod(s, null_mut()) +} + +#[no_mangle] +unsafe extern "C" fn strtod(s: *const c_char, endptr: *mut *mut c_char) -> c_double { + str_to_float_ext(s, endptr) as _ +} + +#[no_mangle] +unsafe extern "C" fn strtof(s: *const c_char, endptr: *mut *mut c_char) -> c_float { + str_to_float_ext(s, endptr) as _ +} + +#[no_mangle] +unsafe extern "C" fn strtold(s: *const c_char, endptr: *mut *mut c_char) -> c_double { + str_to_float_ext(s, endptr) as _ +} + +// ints + +#[no_mangle] +unsafe extern "C" fn atoi(s: *const c_char) -> c_int { + str_to_int_ext(s, null_mut(), 10) +} + +#[no_mangle] +unsafe extern "C" fn atol(s: *const c_char) -> c_long { + str_to_int_ext(s, null_mut(), 10) +} + +#[no_mangle] +unsafe extern "C" fn atoll(s: *const c_char) -> c_longlong { + str_to_int_ext(s, null_mut(), 10) +} + +#[no_mangle] +unsafe extern "C" fn strtol(s: *const c_char, endptr: *mut *mut c_char, base: c_int) -> c_long { + str_to_int_ext(s, endptr, base) +} + +#[no_mangle] +unsafe extern "C" fn strtoll( + s: *const c_char, + endptr: *mut *mut c_char, + base: c_int, +) -> c_longlong { + str_to_int_ext(s, endptr, base) +} + +#[no_mangle] +unsafe extern "C" fn strtoul(s: *const c_char, endptr: *mut *mut c_char, base: c_int) -> c_ulong { + str_to_uint_ext(s, endptr, base) +} + +#[no_mangle] +unsafe extern "C" fn strtoull( + s: *const c_char, + endptr: *mut *mut c_char, + base: c_int, +) -> c_ulonglong { + str_to_uint_ext(s, endptr, base) +} + +#[derive(Clone, Copy, PartialEq)] +enum Radix { + Oct, + Dec, + Hex, +} + +trait StrToInt { + type Unsigned: StrToUint; + const ZERO: Self; + + fn from_unsigned(value: Self::Unsigned, sign: bool) -> Self; +} + +trait StrToUint { + const ZERO: Self; + fn append_digit(self, digit: u8, radix: Radix) -> Self; +} + +impl Radix { + pub fn from_c(c: c_int) -> Option { + match c { + 8 => Some(Self::Oct), + 10 => Some(Self::Dec), + 16 => Some(Self::Hex), + _ => None, + } + } + + pub const fn multiplier(&self) -> u8 { + match self { + Self::Oct => 8, + Self::Dec => 10, + Self::Hex => 16, + } + } +} + +impl StrToInt for i32 { + type Unsigned = u32; + const ZERO: Self = 0; + + fn from_unsigned(value: Self::Unsigned, sign: bool) -> Self { + match sign { + true => value as Self, + false => -(value as Self), + } + } +} + +impl StrToInt for i64 { + type Unsigned = u64; + const ZERO: Self = 0; + + fn from_unsigned(value: Self::Unsigned, sign: bool) -> Self { + match sign { + true => value as Self, + false => -(value as Self), + } + } +} + +impl StrToUint for u32 { + const ZERO: Self = 0; + + fn append_digit(self, digit: u8, radix: Radix) -> Self { + let mul = radix.multiplier(); + self * (mul as Self) + (digit as Self) + } +} + +impl StrToUint for u64 { + const ZERO: Self = 0; + + fn append_digit(self, digit: u8, radix: Radix) -> Self { + let mul = radix.multiplier(); + self * (mul as Self) + (digit as Self) + } +} + +unsafe fn str_to_int_ext( + src: *const c_char, + endptr: *mut *mut c_char, + base: c_int, +) -> I { + let src = NonNull::new(src.cast_mut()).expect("str_to_int: NULL string"); + let radix = Radix::from_c(base); + let (value, end) = str_to_int(src.cast(), radix); + if !endptr.is_null() { + endptr.write(end.as_ptr().cast()); + } + value +} + +unsafe fn str_to_uint_ext( + src: *const c_char, + endptr: *mut *mut c_char, + base: c_int, +) -> U { + let src = NonNull::new(src.cast_mut()).expect("str_to_uint: NULL string"); + let radix = Radix::from_c(base); + let (value, end) = str_to_uint(src.cast(), radix, true); + if !endptr.is_null() { + endptr.write(end.as_ptr().cast()); + } + value +} + +unsafe fn str_to_float_ext(src: *const c_char, endptr: *mut *mut c_char) -> f64 { + let src = NonNull::new(src.cast_mut()).expect("str_to_float: NULL string"); + let (value, end) = str_to_float(src.cast()); + if !endptr.is_null() { + endptr.write(end.as_ptr().cast()); + } + value +} + +unsafe fn str_to_int(mut src: NonNull, radix: Option) -> (I, NonNull) { + // Skip whitespace + while src.read().is_ascii_whitespace() { + src = src.add(1); + } + + // Get sign + let sign = match src.read() { + b'+' => { + src = src.add(1); + true + } + b'-' => { + src = src.add(1); + false + } + _ => true, + }; + + let (unsigned, endptr) = str_to_uint::(src, radix, false); + + (I::from_unsigned(unsigned, sign), endptr) +} + +unsafe fn str_to_uint( + mut src: NonNull, + radix: Option, + skip_whitespace: bool, +) -> (U, NonNull) { + if skip_whitespace { + // Skip whitespace + while src.read().is_ascii_whitespace() { + src = src.add(1); + } + } + + // Guess radix if None + let ch = src.read(); + let radix = match (ch, radix) { + (b'0', None) => { + let ch = src.add(1).read(); + if ch == b'x' || ch == b'X' { + src = src.add(2); + Radix::Hex + } else { + Radix::Oct + } + } + (_, None) => { + // Not 0, 0x or 0X + Radix::Dec + } + (b'0', Some(Radix::Hex)) => { + let ch = src.add(1).read(); + if ch == b'x' || ch == b'X' { + src = src.add(2); + } + Radix::Hex + } + (_, Some(radix)) => radix, + }; + + let mut value = U::ZERO; + + loop { + let ch = src.read(); + if ch == 0 { + break; + } + + let digit = match (ch, radix) { + (b'0'..=b'9', Radix::Dec | Radix::Hex) => ch - b'0', + (b'0'..=b'7', Radix::Oct) => ch - b'0', + (b'a'..=b'f', Radix::Hex) => ch - b'a' + 10, + (b'A'..=b'F', Radix::Hex) => ch - b'A' + 10, + (_, _) => return (value, src), + }; + + value = value.append_digit(digit, radix); + src = src.add(1); + } + + (value, src) +} + +unsafe fn str_to_float(mut src: NonNull) -> (f64, NonNull) { + // Skip leading whitespace + while src.read().is_ascii_whitespace() { + src = src.add(1); + } + + // Get sign + let sign = match src.read() { + b'+' => { + src = src.add(1); + true + } + b'-' => { + src = src.add(1); + false + } + _ => true, + }; + + // Either: + // * decimal float + // * hex float + // * INF/INFINITY, disregarding case + // * NAN, disregarding case + + if is_nan(src) { + return (f64::NAN, src.add(3)); + } + if let Some(len) = is_inf(src) { + return match sign { + false => (f64::NEG_INFINITY, src.add(len)), + true => (f64::INFINITY, src.add(len)), + }; + } + + match is_hex(src) { + true => unimplemented!(), + false => str_to_float_dec(src, sign), + } +} + +unsafe fn str_to_float_dec(mut src: NonNull, sign: bool) -> (f64, NonNull) { + // 123.456e123 + + let mut int_part = 0u64; + let mut frac_part = 0u64; + let mut frac_digits = 0; + + // Read integer part + loop { + let ch = src.read(); + let digit = match ch { + b'0'..=b'9' => ch - b'0', + b'.' | b'E' | b'e' => break, + _ => return (assemble_float(sign, int_part, 0, 0), src), + }; + + int_part = int_part * 10 + digit as u64; + src = src.add(1); + } + + // maybe . + let ch = src.read(); + if ch == b'.' { + src = src.add(1); + + // Parse fractional part + loop { + let ch = src.read(); + let digit = match ch { + b'0'..=b'9' => ch - b'0', + b'e' | b'E' => break, + _ => return (assemble_float(sign, int_part, frac_part, frac_digits), src), + }; + + frac_part = frac_part * 10 + digit as u64; + frac_digits += 1; + src = src.add(1); + } + } + + // maybe exponent + let ch = src.read(); + if ch == b'e' || ch == b'E' { + todo!() + } + + todo!() +} + +unsafe fn is_nan(s: NonNull) -> bool { + cstr_matches_insensitive(s, b"nan") +} + +unsafe fn is_inf(s: NonNull) -> Option { + const INFINITY: &[u8] = b"infinity"; + const INF: &[u8] = b"inf"; + + if cstr_matches_insensitive(s, INFINITY) { + Some(INFINITY.len()) + } else if cstr_matches_insensitive(s, INF) { + Some(INF.len()) + } else { + None + } +} + +unsafe fn is_hex(s: NonNull) -> bool { + cstr_matches_insensitive(s, b"0x") +} + +fn powi(f: f64, i: i32) -> f64 { + let mut v = 1.0; + for _ in 0..i { + v *= f; + } + v +} + +fn assemble_float(sign: bool, i: u64, f: u64, n: i32) -> f64 { + let i = i as f64; + let f = f as f64 / powi(10.0, n); + let v = i + f; + match sign { + false => -v, + true => v, + } +} diff --git a/userspace/lib/ygglibc/src/headers/stdlib/io.rs b/userspace/lib/ygglibc/src/headers/stdlib/io.rs new file mode 100644 index 00000000..7f6fdffa --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/stdlib/io.rs @@ -0,0 +1,8 @@ + +// int grantpt(int); +// char *mkdtemp(char *); +// int mkstemp(char *); +// int posix_openpt(int); +// char *ptsname(int); +// char *realpath(const char *restrict, char *restrict); +// int unlockpt(int); diff --git a/userspace/lib/ygglibc/src/headers/stdlib/malloc.rs b/userspace/lib/ygglibc/src/headers/stdlib/malloc.rs new file mode 100644 index 00000000..e2c8516f --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/stdlib/malloc.rs @@ -0,0 +1,47 @@ +use core::{ + ffi::{c_int, c_void}, + ptr::NonNull, +}; + +use crate::{ + allocator, + error::{CPtrResult, OptionExt}, + headers::errno, +}; + +#[no_mangle] +unsafe extern "C" fn calloc(size: usize, nmemb: usize) -> CPtrResult { + let size = size.checked_mul(nmemb).e_ok_or(errno::ENOMEM)?; + let ptr = allocator::c_alloc(size, 16, true)?; + CPtrResult::success(ptr) +} + +#[no_mangle] +unsafe extern "C" fn free(ptr: *mut c_void) { + match NonNull::new(ptr) { + Some(ptr) => allocator::c_free(ptr), + None => (), + } +} + +#[no_mangle] +unsafe extern "C" fn malloc(size: usize) -> CPtrResult { + let ptr = allocator::c_alloc(size, 16, false)?; + CPtrResult::success(ptr) +} + +#[no_mangle] +unsafe extern "C" fn posix_memalign( + _memptr: *mut *mut c_void, + _alignment: usize, + _size: usize, +) -> c_int { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn realloc(ptr: *mut c_void, size: usize) -> CPtrResult { + let old_ptr = NonNull::new(ptr); + let new_ptr = allocator::c_realloc(old_ptr, size)?; + CPtrResult::success(new_ptr) +} diff --git a/userspace/lib/ygglibc/src/headers/stdlib/math.rs b/userspace/lib/ygglibc/src/headers/stdlib/math.rs new file mode 100644 index 00000000..df4d6cf1 --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/stdlib/math.rs @@ -0,0 +1,61 @@ +use core::ffi::{c_int, c_long, c_longlong}; + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[repr(C)] +pub struct div_t { + pub quot: c_int, + pub rem: c_int +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[repr(C)] +pub struct ldiv_t { + pub quot: c_long, + pub rem: c_long +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[repr(C)] +pub struct lldiv_t { + pub quot: c_longlong, + pub rem: c_longlong +} + +#[no_mangle] +unsafe extern "C" fn abs(v: c_int) -> c_int { + v.abs() +} + +#[no_mangle] +unsafe extern "C" fn labs(v: c_long) -> c_long { + v.abs() +} + +#[no_mangle] +unsafe extern "C" fn llabs(v: c_longlong) -> c_longlong { + v.abs() +} + +#[no_mangle] +unsafe extern "C" fn div(x: c_int, y: c_int) -> div_t { + div_t { + quot: x / y, + rem: x % y + } +} + +#[no_mangle] +unsafe extern "C" fn ldiv(x: c_long, y: c_long) -> ldiv_t { + ldiv_t { + quot: x / y, + rem: x % y + } +} + +#[no_mangle] +unsafe extern "C" fn lldiv(x: c_longlong, y: c_longlong) -> lldiv_t { + lldiv_t { + quot: x / y, + rem: x % y + } +} diff --git a/userspace/lib/ygglibc/src/headers/stdlib/mod.rs b/userspace/lib/ygglibc/src/headers/stdlib/mod.rs index 9e4f79e9..57c412a3 100644 --- a/userspace/lib/ygglibc/src/headers/stdlib/mod.rs +++ b/userspace/lib/ygglibc/src/headers/stdlib/mod.rs @@ -1,8 +1,28 @@ -use core::ffi::c_void; +use core::ffi::c_int; -use crate::error::CPtrResult; +mod conv; +mod io; +mod malloc; +mod math; +mod process; +mod random; +mod util; +mod wide; -#[no_mangle] -pub unsafe extern "C" fn malloc(size: usize) -> CPtrResult { - todo!() -} +pub const EXIT_SUCCESS: c_int = 0; +pub const EXIT_FAILURE: c_int = 1; + +pub const MB_CUR_MAX: c_int = 4; + +// TODO +// {RAND_MAX} +// Maximum value returned by rand(); at least 32767. +// +// WEXITSTATUS +// WIFEXITED +// WIFSIGNALED +// WIFSTOPPED +// WNOHANG +// WSTOPSIG +// WTERMSIG +// WUNTRACED diff --git a/userspace/lib/ygglibc/src/headers/stdlib/process.rs b/userspace/lib/ygglibc/src/headers/stdlib/process.rs new file mode 100644 index 00000000..0000ac73 --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/stdlib/process.rs @@ -0,0 +1,61 @@ +use core::ffi::{c_char, c_int}; + +use crate::{ + error::{CIntZeroResult, CPtrResult}, + process, +}; + +#[no_mangle] +unsafe extern "C" fn _Exit(status: c_int) -> ! { + process::c_exit_immediately(status) +} + +#[no_mangle] +unsafe extern "C" fn abort() -> ! { + process::abort() +} + +#[no_mangle] +unsafe extern "C" fn atexit(_f: extern "C" fn()) -> CIntZeroResult { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn exit(status: c_int) -> ! { + process::c_exit(status) +} + +#[no_mangle] +unsafe extern "C" fn getenv(_name: *const c_char) -> CPtrResult { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn putenv(_value: *const c_char) -> CPtrResult { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn setenv( + _name: *const c_char, + _value: *const c_char, + _overwrite: c_int, +) -> CIntZeroResult { + todo!() +} + +// Deprecated +#[no_mangle] +unsafe extern "C" fn setkey(_key: *const c_char) { + unimplemented!() +} + +#[no_mangle] +unsafe extern "C" fn system(_command: *const c_char) -> CIntZeroResult { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn unsetenv(_name: *const c_char) -> c_int { + todo!() +} diff --git a/userspace/lib/ygglibc/src/headers/stdlib/random.rs b/userspace/lib/ygglibc/src/headers/stdlib/random.rs new file mode 100644 index 00000000..a0b44889 --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/stdlib/random.rs @@ -0,0 +1,17 @@ + +// double drand48(void); +// double erand48(unsigned short [3]); +// char *initstate(unsigned, char *, size_t); +// long jrand48(unsigned short [3]); +// void lcong48(unsigned short [7]); +// long lrand48(void); +// long mrand48(void); +// long nrand48(unsigned short [3]); +// int rand(void); +// int rand_r(unsigned *); +// long random(void); +// unsigned short *seed48(unsigned short [3]); +// char *setstate(char *); +// void srand(unsigned); +// void srand48(long); +// void srandom(unsigned); diff --git a/userspace/lib/ygglibc/src/headers/stdlib/util.rs b/userspace/lib/ygglibc/src/headers/stdlib/util.rs new file mode 100644 index 00000000..7941b545 --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/stdlib/util.rs @@ -0,0 +1,134 @@ +use core::{ + cmp::Ordering, + ffi::{c_int, c_void}, +}; + +struct Array { + base: *mut c_void, + size: usize, + nmemb: usize, +} + +impl Array { + unsafe fn new(base: *mut c_void, nmemb: usize, size: usize) -> Option { + if nmemb == 0 || size == 0 || base.is_null() { + return None; + } + Some(Self { base, nmemb, size }) + } + + fn element(&mut self, i: usize) -> *mut c_void { + if i >= self.nmemb { + panic!("i = {}, nmemb = {}", i, self.nmemb); + } + unsafe { self.base.add(i * self.size) } + } + + fn partition Ordering>( + &mut self, + lower: usize, + upper: usize, + cmp: &F, + ) -> usize { + if lower >= upper { + panic!() + } + let pivot_ptr = self.element(lower); + let mut i = lower; + let mut j = upper; + + while i < j { + while i <= upper - 1 && cmp(self.element(i), pivot_ptr).is_le() { + i += 1; + } + while j >= lower + 1 && cmp(self.element(j), pivot_ptr).is_gt() { + j -= 1; + } + + if i < j { + self.swap(i, j); + } + } + self.swap(lower, j); + j + } + + fn swap(&mut self, i: usize, j: usize) { + let el_i = self.element(i) as *mut u8; + let el_j = self.element(j) as *mut u8; + unsafe { + for i in 0..self.size { + core::ptr::swap(el_i.add(i), el_j.add(i)); + } + } + } + + fn sort_inner Ordering>( + &mut self, + lower: usize, + upper: usize, + cmp: &F, + ) { + if lower < upper { + let partition = self.partition(lower, upper, cmp); + + if partition != 0 { + self.sort_inner(lower, partition - 1, cmp); + } + self.sort_inner(partition + 1, upper, cmp); + } + } + + fn sort Ordering>(&mut self, cmp: &F) { + if self.nmemb != 0 { + self.sort_inner(0, self.nmemb - 1, cmp); + } + } +} + +pub type qsort_compar_fn_t = extern "C" fn(*const c_void, *const c_void) -> c_int; +pub type qsort_compar_r_fn_t = extern "C" fn(*const c_void, *const c_void, *mut c_void) -> c_int; + +pub type bsearch_compar_fn_t = extern "C" fn(*const c_void, *const c_void) -> c_int; + +#[no_mangle] +unsafe extern "C" fn bsearch( + _key: *const c_void, + _base: *const c_void, + _nmemb: usize, + _size: usize, + _compar: bsearch_compar_fn_t, +) -> *mut c_void { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn qsort( + base: *mut c_void, + nmemb: usize, + size: usize, + compar: qsort_compar_fn_t, +) { + let Some(mut array) = Array::new(base, nmemb, size) else { + return; + }; + + array.sort(&|a, b| compar(a, b).cmp(&0)); +} + +#[no_mangle] +unsafe extern "C" fn qsort_r( + base: *mut c_void, + nmemb: usize, + size: usize, + compar: qsort_compar_r_fn_t, + arg: *mut c_void, +) { + let Some(mut array) = Array::new(base, nmemb, size) else { + return; + }; + + array.sort(&|a, b| compar(a, b, arg).cmp(&0)); +} + +// int getsubopt(char **, char *const *, char **); diff --git a/userspace/lib/ygglibc/src/headers/stdlib/wide.rs b/userspace/lib/ygglibc/src/headers/stdlib/wide.rs new file mode 100644 index 00000000..bd5a9f7b --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/stdlib/wide.rs @@ -0,0 +1,6 @@ + +// int mblen(const char *, size_t); +// size_t mbstowcs(wchar_t *restrict, const char *restrict, size_t); +// int mbtowc(wchar_t *restrict, const char *restrict, size_t); +// size_t wcstombs(char *restrict, const wchar_t *restrict, size_t); +// int wctomb(char *, wchar_t); diff --git a/userspace/lib/ygglibc/src/headers/sys_stat/cbindgen.toml b/userspace/lib/ygglibc/src/headers/sys_stat/cbindgen.toml new file mode 100644 index 00000000..9a004b15 --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/sys_stat/cbindgen.toml @@ -0,0 +1,19 @@ +language = "C" +style = "Tag" + +sys_includes = [ + "stddef.h", + "stdint.h", + "sys/types.h", + "time.h", + "bits/sys/stat.h" +] +no_includes = true + +include_guard = "_SYS_STAT_H" + +usize_type = "size_t" +isize_type = "ssize_t" + +[export] +include = ["stat"] diff --git a/userspace/lib/ygglibc/src/headers/sys_stat/mod.rs b/userspace/lib/ygglibc/src/headers/sys_stat/mod.rs new file mode 100644 index 00000000..dfe8e001 --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/sys_stat/mod.rs @@ -0,0 +1,221 @@ +use core::ffi::{c_char, c_int}; + +use yggdrasil_rt::io::{FileAttr, FileMode, FileType}; + +use crate::{ + error::CIntZeroResult, + util::{self, PointerStrExt}, +}; + +use super::{ + fcntl::{AT_FDCWD, AT_SYMLINK_NOFOLLOW}, + sys_time::__ygg_timespec_t, + sys_types::{blkcnt_t, blksize_t, dev_t, gid_t, ino_t, mode_t, nlink_t, off_t, uid_t}, +}; + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +#[repr(C)] +pub struct stat { + pub st_dev: dev_t, + pub st_ino: ino_t, + pub st_mode: mode_t, + pub st_nlink: nlink_t, + pub st_uid: uid_t, + pub st_gid: gid_t, + pub st_rdev: dev_t, + pub st_size: off_t, + pub st_atim: __ygg_timespec_t, + pub st_mtim: __ygg_timespec_t, + pub st_ctim: __ygg_timespec_t, + pub st_blksize: blksize_t, + pub st_blocks: blkcnt_t, +} + +pub const S_IFMT: c_int = 0xF << 16; +pub const S_IFBLK: c_int = 1 << 16; +pub const S_IFCHR: c_int = 2 << 16; +pub const S_IFIFO: c_int = 3 << 16; +pub const S_IFREG: c_int = 4 << 16; +pub const S_IFDIR: c_int = 5 << 16; +pub const S_IFLNK: c_int = 6 << 16; +pub const S_IFSOCK: c_int = 7 << 16; + +pub const S_IRWXU: c_int = 0x7 << 6; +pub const S_IRUSR: c_int = 0x4 << 6; +pub const S_IWUSR: c_int = 0x2 << 6; +pub const S_IXUSR: c_int = 0x1 << 6; + +pub const S_IRWXG: c_int = 0x7 << 3; +pub const S_IRGRP: c_int = 0x4 << 3; +pub const S_IWGRP: c_int = 0x2 << 3; +pub const S_IXGRP: c_int = 0x1 << 3; + +pub const S_IRWXO: c_int = 0x7 << 0; +pub const S_IROTH: c_int = 0x4 << 0; +pub const S_IWOTH: c_int = 0x2 << 0; +pub const S_IXOTH: c_int = 0x1 << 0; + +pub const S_ISUID: c_int = 1 << 11; +pub const S_ISGID: c_int = 1 << 10; +pub const S_ISVTX: c_int = 1 << 9; + +impl From for stat { + fn from(value: FileAttr) -> Self { + // TODO no translation for st_dev/st_rdev/st_ino/etc + let mut st = Self::default(); + st.st_mode = (value.mode.bits() & 0o777) as _; + match value.ty { + FileType::Block => st.st_mode |= S_IFBLK, + FileType::Char => st.st_mode |= S_IFCHR, + FileType::File => st.st_mode |= S_IFREG, + FileType::Directory => st.st_mode |= S_IFDIR, + FileType::Symlink => st.st_mode |= S_IFLNK, + } + st.st_size = value.size.try_into().unwrap(); + // TODO + st.st_uid = u32::from(value.uid).try_into().unwrap(); + st.st_gid = u32::from(value.gid).try_into().unwrap(); + // TODO + st.st_blksize = 512; + st.st_blocks = (st.st_size + 511) / 512; + // TODO + st.st_nlink = 1; + + st + } +} + +#[no_mangle] +unsafe extern "C" fn chmod(_pathname: *const c_char, _mode: mode_t) -> c_int { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn fchmod(_fd: c_int, _mode: mode_t) -> c_int { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn fchmodat( + _fd: c_int, + _pathname: *const c_char, + _mode: mode_t, + _opt: c_int, +) -> c_int { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn umask(_mode: mode_t) -> mode_t { + todo!() + // let new = FileMode::new((mode as u32) & 0o777); + // let old = process::update_umask(new); + // old.bits() as mode_t +} + +// Create stuff + +#[no_mangle] +unsafe extern "C" fn mkdir(pathname: *const c_char, mode: mode_t) -> CIntZeroResult { + mkdirat(AT_FDCWD, pathname, mode) +} + +#[no_mangle] +unsafe extern "C" fn mkdirat(atfd: c_int, pathname: *const c_char, mode: mode_t) -> CIntZeroResult { + let _pathname = pathname.ensure_str(); + let _atfd = util::at_fd(atfd)?; + let _mode = FileMode::new((mode & 0o777) as u32); + + todo!() + // io::create_directory(atfd, pathname, mode)?; + + // CIntZeroResult::OK +} + +#[no_mangle] +unsafe extern "C" fn mkfifo(_pathname: *const c_char, _mode: mode_t) -> c_int { + unimplemented!() +} + +#[no_mangle] +unsafe extern "C" fn mkfifoat(_atfd: c_int, _pathname: *const c_char, _mode: mode_t) -> c_int { + unimplemented!() +} + +#[no_mangle] +unsafe extern "C" fn mknod(_pathname: *const c_char, _mode: mode_t, _dev: dev_t) -> c_int { + unimplemented!() +} + +#[no_mangle] +unsafe extern "C" fn mknodat( + _atfd: c_int, + _pathname: *const c_char, + _mode: mode_t, + _dev: dev_t, +) -> c_int { + unimplemented!() +} + +// File status + +#[no_mangle] +unsafe extern "C" fn fstat(_fd: c_int, _statbuf: *mut stat) -> CIntZeroResult { + todo!() + // let fd = RawFd::e_try_from(fd)?; + // let attr = io::get_metadata(Some(fd), "", false)?; + + // if let Some(statbuf) = statbuf.as_mut() { + // *statbuf = attr.into(); + // } + + // CIntZeroResult::OK +} + +#[no_mangle] +unsafe extern "C" fn fstatat( + atfd: c_int, + pathname: *const c_char, + _statbuf: *mut stat, + opt: c_int, +) -> CIntZeroResult { + let _pathname = pathname.ensure_str(); + let _atfd = util::at_fd(atfd)?; + let _follow = opt & AT_SYMLINK_NOFOLLOW == 0; + + todo!() + // let attr = io::get_metadata(atfd, pathname, follow)?; + + // if let Some(statbuf) = statbuf.as_mut() { + // *statbuf = attr.into(); + // } + + // CIntZeroResult::OK +} + +#[no_mangle] +unsafe extern "C" fn lstat(pathname: *const c_char, statbuf: *mut stat) -> CIntZeroResult { + fstatat(AT_FDCWD, pathname, statbuf, AT_SYMLINK_NOFOLLOW) +} + +#[no_mangle] +unsafe extern "C" fn stat(pathname: *const c_char, statbuf: *mut stat) -> CIntZeroResult { + fstatat(AT_FDCWD, pathname, statbuf, 0) +} + +// File time updates + +#[no_mangle] +unsafe extern "C" fn futimens(_fd: c_int, _times: *const __ygg_timespec_t) -> c_int { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn utimensat( + _atfd: c_int, + _pathname: *const c_char, + _times: *const __ygg_timespec_t, + _opt: c_int, +) -> c_int { + todo!() +} diff --git a/userspace/lib/ygglibc/src/headers/sys_time/cbindgen.toml b/userspace/lib/ygglibc/src/headers/sys_time/cbindgen.toml new file mode 100644 index 00000000..87edadaa --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/sys_time/cbindgen.toml @@ -0,0 +1,14 @@ +language = "C" +style = "Tag" + +sys_includes = ["stdarg.h", "stddef.h", "stdint.h", "sys/types.h"] +no_includes = true + +include_guard = "_SYS_TIME_H" + +usize_type = "size_t" +isize_type = "ssize_t" + +[export] +include = ["timeval", "timespec", "__ygg_timeval_t", "__ygg_timespec_t"] +exclude = [] diff --git a/userspace/lib/ygglibc/src/headers/sys_time/mod.rs b/userspace/lib/ygglibc/src/headers/sys_time/mod.rs new file mode 100644 index 00000000..4e6be1b0 --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/sys_time/mod.rs @@ -0,0 +1,20 @@ +use core::ffi::c_long; + +use super::sys_types::{suseconds_t, time_t}; + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +#[repr(C)] +pub struct timeval { + pub tv_sec: time_t, + pub tv_usec: suseconds_t, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +#[repr(C)] +pub struct timespec { + pub tv_sec: time_t, + pub tv_nsec: c_long, +} + +pub type __ygg_timeval_t = timeval; +pub type __ygg_timespec_t = timespec; diff --git a/userspace/lib/ygglibc/src/headers/sys_types/mod.rs b/userspace/lib/ygglibc/src/headers/sys_types/mod.rs index 6f2f1f6c..474c9a88 100644 --- a/userspace/lib/ygglibc/src/headers/sys_types/mod.rs +++ b/userspace/lib/ygglibc/src/headers/sys_types/mod.rs @@ -32,3 +32,7 @@ pub struct time_t(pub i64); pub type off_t = i64; pub type suseconds_t = c_ulong; + +impl time_t { + pub const INVALID: Self = Self(i64::MAX); +} diff --git a/userspace/lib/ygglibc/src/headers/time/cbindgen.toml b/userspace/lib/ygglibc/src/headers/time/cbindgen.toml new file mode 100644 index 00000000..b195d49b --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/time/cbindgen.toml @@ -0,0 +1,20 @@ +language = "C" +style = "Tag" + +sys_includes = [ + "stddef.h", + "stdint.h", + "locale.h", + "sys/types.h", + "sys/time.h", + "bits/time.h", +] +no_includes = true + +include_guard = "_TIME_H" + +usize_type = "size_t" +isize_type = "ssize_t" + +[export] +include = ["tm", "itimerspec"] diff --git a/userspace/lib/ygglibc/src/headers/time/convert.rs b/userspace/lib/ygglibc/src/headers/time/convert.rs new file mode 100644 index 00000000..0739f9de --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/time/convert.rs @@ -0,0 +1,64 @@ +use core::{cmp::Ordering, mem::MaybeUninit, ptr::null_mut}; + +use chrono::Utc; + +use crate::headers::{sys_types::time_t, time::tm}; + +use super::{get_timezone, CDateTime}; + +#[no_mangle] +unsafe extern "C" fn gmtime(tp: *const time_t) -> *mut tm { + static mut BUFFER: MaybeUninit = MaybeUninit::uninit(); + gmtime_r(tp, BUFFER.as_mut_ptr()) +} + +#[no_mangle] +unsafe extern "C" fn gmtime_r(tp: *const time_t, timep: *mut tm) -> *mut tm { + let tp = tp.as_ref().unwrap(); + let timep = timep.as_mut().unwrap(); + + match tp.to_datetime(Utc) { + Some(t) => { + *timep = t.into(); + timep + } + None => null_mut(), + } +} + +#[no_mangle] +unsafe extern "C" fn localtime(tp: *const time_t) -> *mut tm { + static mut BUFFER: MaybeUninit = MaybeUninit::uninit(); + localtime_r(tp, BUFFER.as_mut_ptr()) +} + +#[no_mangle] +unsafe extern "C" fn localtime_r(tp: *const time_t, timep: *mut tm) -> *mut tm { + let tp = tp.as_ref().unwrap(); + let timep = timep.as_mut().unwrap(); + + let tz = get_timezone(); + match tp.to_datetime(tz) { + Some(t) => { + *timep = t.into(); + timep + } + None => null_mut(), + } +} + +#[no_mangle] +unsafe extern "C" fn mktime(timep: *const tm) -> time_t { + let timep = timep.as_ref().unwrap(); + let _is_dst = match timep.tm_isdst.cmp(&0) { + Ordering::Less => todo!(), + Ordering::Equal => false, + Ordering::Greater => true, + }; + + let tz = get_timezone(); + match timep.to_datetime(tz) { + Some(t) => time_t(t.timestamp() as _), + None => time_t::INVALID, + } +} diff --git a/userspace/lib/ygglibc/src/headers/time/mod.rs b/userspace/lib/ygglibc/src/headers/time/mod.rs new file mode 100644 index 00000000..50e8c227 --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/time/mod.rs @@ -0,0 +1,113 @@ +use core::{ + ffi::{c_char, c_int, c_long}, + ptr::null_mut, + time::Duration, +}; + +use chrono::{DateTime, Datelike, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Timelike, Utc}; + +use super::{sys_time::__ygg_timespec_t, sys_types::time_t}; + +mod convert; +mod string; +mod timer; +mod util; + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +#[repr(C)] +pub struct itimerspec { + pub it_interval: __ygg_timespec_t, + pub it_value: __ygg_timespec_t, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +#[repr(C)] +pub struct tm { + pub tm_sec: c_int, + pub tm_min: c_int, + pub tm_hour: c_int, + pub tm_mday: c_int, + pub tm_mon: c_int, + pub tm_year: c_int, + pub tm_wday: c_int, + pub tm_yday: c_int, + pub tm_isdst: c_int, +} + +#[no_mangle] +pub static mut daylight: c_int = 0; +#[no_mangle] +pub static mut timezone: c_long = 0; +#[no_mangle] +pub static mut tzname: *mut *mut c_char = null_mut(); + +pub trait CDateTime { + fn to_naive_datetime(&self) -> Option; + + fn to_datetime(&self, tz: Tz) -> Option> { + self.to_naive_datetime()?.and_local_timezone(tz).latest() + } +} + +impl tm { + pub fn naive_date(&self) -> Option { + NaiveDate::from_ymd_opt( + self.tm_year + 1900, + u32::try_from(self.tm_mon).ok()? + 1, + u32::try_from(self.tm_mday).ok()?, + ) + } + + pub fn naive_time(&self) -> Option { + NaiveTime::from_hms_opt( + u32::try_from(self.tm_hour).ok()?, + u32::try_from(self.tm_min).ok()?, + u32::try_from(self.tm_sec).ok()?, + ) + } +} + +impl CDateTime for tm { + fn to_naive_datetime(&self) -> Option { + let date = self.naive_date()?; + let time = self.naive_time()?; + + Some(NaiveDateTime::new(date, time)) + } +} + +impl CDateTime for time_t { + fn to_naive_datetime(&self) -> Option { + NaiveDateTime::from_timestamp_opt(self.0, 0) + } +} + +impl From for tm { + fn from(value: T) -> Self { + Self { + tm_sec: value.second() as _, + tm_min: value.minute() as _, + tm_hour: value.hour() as _, + tm_mday: value.day() as _, + tm_mon: value.month0() as _, + tm_year: value.year() - 1900, + tm_wday: value.weekday().num_days_from_sunday() as _, + tm_yday: value.ordinal0() as _, + tm_isdst: 0, + } + } +} + +impl From<__ygg_timespec_t> for Duration { + fn from(value: __ygg_timespec_t) -> Self { + Self::new( + value.tv_sec.0.try_into().unwrap(), + value.tv_nsec.try_into().unwrap(), + ) + } +} + +pub unsafe fn get_timezone() -> impl TimeZone { + // TODO get it from C + Utc +} diff --git a/userspace/lib/ygglibc/src/headers/time/string.rs b/userspace/lib/ygglibc/src/headers/time/string.rs new file mode 100644 index 00000000..9922b189 --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/time/string.rs @@ -0,0 +1,112 @@ +use core::{ffi::c_char, ptr::{null_mut, NonNull}}; + +use chrono::{Datelike, TimeZone, Timelike, Utc}; + +use crate::{headers::{locale::locale_t, sys_types::time_t}, util::CStringWriter}; + +use super::{get_timezone, tm, CDateTime}; + +unsafe fn fmt_time( + t: &T, + tz: Tz, + buffer: *mut c_char, +) -> Option> { + const MONTHS: &[&str] = &[ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + ]; + const WDAYS: &[&str] = &["Sun", "Mon", "Wed", "Thu", "Fri", "Sat"]; + + use core::fmt::Write; + + let dt = t.to_datetime(tz)?; + let mut writer = CStringWriter::from_ptr(buffer, usize::MAX)?; + + writeln!( + writer, + "{} {} {:02} {:02}:{:02}:{:02} {:04}", + WDAYS[dt.weekday().num_days_from_sunday() as usize], + MONTHS[dt.month0() as usize], + dt.day(), + dt.hour(), + dt.minute(), + dt.second(), + dt.year() + ) + .ok()?; + + writer.finish() +} + +#[no_mangle] +unsafe extern "C" fn asctime(timep: *const tm) -> *mut c_char { + static mut BUFFER: [c_char; 32] = [0; 32]; + asctime_r(timep, BUFFER.as_mut_ptr()) +} + +#[no_mangle] +unsafe extern "C" fn asctime_r(timep: *const tm, buffer: *mut c_char) -> *mut c_char { + let timep = timep.as_ref().unwrap(); + + // Utc, already accounts for timezone + match fmt_time(timep, Utc, buffer) { + Some(ptr) => ptr.as_ptr(), + None => null_mut() + } +} + +#[no_mangle] +unsafe extern "C" fn ctime(tp: *const time_t) -> *mut c_char { + static mut BUFFER: [c_char; 32] = [0; 32]; + ctime_r(tp, BUFFER.as_mut_ptr()) +} + +#[no_mangle] +unsafe extern "C" fn ctime_r(tp: *const time_t, buffer: *mut c_char) -> *mut c_char { + let tp = tp.as_ref().unwrap(); + let tz = get_timezone(); + + match fmt_time(tp, tz, buffer) { + Some(ptr) => ptr.as_ptr(), + None => null_mut() + } +} + +#[no_mangle] +unsafe extern "C" fn getdate(_string: *const c_char) -> *mut tm { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn getdate_r(_string: *const c_char, _timep: *mut tm) -> *mut tm { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn strftime( + _dst: *mut c_char, + _len: usize, + _format: *const c_char, + _timep: *const tm, +) -> usize { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn strftime_l( + _dst: *mut c_char, + _len: usize, + _format: *const c_char, + _timep: *const tm, + _locale: locale_t, +) -> usize { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn strptime( + _src: *const c_char, + _format: *const c_char, + _timep: *mut tm, +) -> *mut c_char { + todo!() +} diff --git a/userspace/lib/ygglibc/src/headers/time/timer.rs b/userspace/lib/ygglibc/src/headers/time/timer.rs new file mode 100644 index 00000000..c9934246 --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/time/timer.rs @@ -0,0 +1,55 @@ +use core::ffi::c_int; + +use crate::headers::{ + sys_time::__ygg_timespec_t, + sys_types::{clock_t, clockid_t, pid_t}, +}; + +/* +TODO +int timer_create(clockid_t, struct sigevent *restrict, + timer_t *restrict); +int timer_delete(timer_t); +int timer_getoverrun(timer_t); +int timer_gettime(timer_t, struct itimerspec *); +int timer_settime(timer_t, int, const struct itimerspec *restrict, + struct itimerspec *restrict); +*/ + +#[no_mangle] +unsafe extern "C" fn clock() -> clock_t { + // TODO + 0 +} + +#[no_mangle] +unsafe extern "C" fn clock_getcpuclockid(_pid: pid_t, _clock_id_ptr: *mut clockid_t) -> c_int { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn clock_getres(_clock_id: clockid_t, _ts: *mut __ygg_timespec_t) -> c_int { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn clock_gettime(_clock_id: clockid_t, _ts: *mut __ygg_timespec_t) -> c_int { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn clock_settime(_clock_id: clockid_t, _ts: *const __ygg_timespec_t) -> c_int { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn clock_nanosleep( + _clock_id: clockid_t, + _flags: c_int, + _rqtp: *const __ygg_timespec_t, + _rmtp: *mut __ygg_timespec_t, +) -> c_int { + todo!() +} + +// TODO Timer something diff --git a/userspace/lib/ygglibc/src/headers/time/util.rs b/userspace/lib/ygglibc/src/headers/time/util.rs new file mode 100644 index 00000000..6e0782af --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/time/util.rs @@ -0,0 +1,39 @@ +use core::{ffi::c_double, time::Duration}; + +use crate::{ + error::CIntZeroResult, + headers::{sys_time::__ygg_timespec_t, sys_types::time_t}, + process, +}; + +#[no_mangle] +unsafe extern "C" fn nanosleep( + rqtp: *const __ygg_timespec_t, + rmtp: *mut __ygg_timespec_t, +) -> CIntZeroResult { + let rqtp = rqtp.as_ref().unwrap(); + if let Some(_rmtp) = rmtp.as_mut() { + todo!(); + } + + let amount = Duration::from(*rqtp); + + process::sleep(amount)?; + + CIntZeroResult::SUCCESS +} + +#[no_mangle] +unsafe extern "C" fn time(_tp: *mut time_t) -> time_t { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn tzset() { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn difftime(_a: time_t, _b: time_t) -> c_double { + todo!() +} diff --git a/userspace/lib/ygglibc/src/lib.rs b/userspace/lib/ygglibc/src/lib.rs index ba697924..1f2ad5b5 100644 --- a/userspace/lib/ygglibc/src/lib.rs +++ b/userspace/lib/ygglibc/src/lib.rs @@ -9,36 +9,40 @@ slice_internals )] #![allow(internal_features)] -#![no_std] +#![cfg_attr(not(test), no_std)] extern crate alloc; -use core::{ - ffi::{c_char, c_int}, - panic::PanicInfo, - ptr::null, -}; - -use alloc::vec::Vec; -use yggdrasil_rt::{debug_trace, process::Signal}; +#[cfg(test)] +extern crate std; mod allocator; mod args; mod error; mod io; +mod panic; mod process; mod sync; mod types; mod util; +mod ssp; pub mod headers; #[no_mangle] -unsafe extern "C" fn _start(arg: usize) { +unsafe extern "C" fn __ygglibc_entry(arg: usize) { + use core::{ + ffi::{c_char, c_int}, + ptr::null, + }; + + use alloc::vec::Vec; + extern "C" { fn main(argc: c_int, argv: *const *const c_char, envp: *const *const c_char) -> c_int; } + ssp::init(); io::init(); // Setup args @@ -63,15 +67,3 @@ unsafe extern "C" fn _start(arg: usize) { process::c_exit(status) } - -#[panic_handler] -fn panic_handler(pi: &PanicInfo) -> ! { - debug_trace!("PANIC {:?}", &pi); - - // Kill myself - unsafe { - let pid = yggdrasil_rt::sys::get_pid(); - yggdrasil_rt::sys::send_signal(pid, Signal::Aborted).ok(); - unreachable!() - } -} diff --git a/userspace/lib/ygglibc/src/panic.rs b/userspace/lib/ygglibc/src/panic.rs new file mode 100644 index 00000000..aed38e60 --- /dev/null +++ b/userspace/lib/ygglibc/src/panic.rs @@ -0,0 +1,8 @@ +#[cfg(any(not(test), rust_analyzer))] +#[panic_handler] +fn panic_handler(pi: &core::panic::PanicInfo) -> ! { + yggdrasil_rt::debug_trace!("PANIC {:?}", &pi); + + // Kill myself + super::process::abort(); +} diff --git a/userspace/lib/ygglibc/src/process.rs b/userspace/lib/ygglibc/src/process.rs index ebe0d6df..7af807bf 100644 --- a/userspace/lib/ygglibc/src/process.rs +++ b/userspace/lib/ygglibc/src/process.rs @@ -1,6 +1,6 @@ use core::{ffi::{c_char, c_int, CStr}, ptr::NonNull, time::Duration}; -use yggdrasil_rt::{process::ExitCode, sys as syscall}; +use yggdrasil_rt::{process::{ExitCode, Signal}, sys as syscall}; use crate::{error::EResult, headers::sys_types::pid_t, io::{self, managed::stderr}}; @@ -20,7 +20,9 @@ pub fn sleep(duration: Duration) -> EResult<()> { } pub fn abort() -> ! { - todo!() + let pid = unsafe { syscall::get_pid() }; + unsafe { syscall::send_signal(pid, Signal::Aborted).ok() }; + unreachable!() } pub fn c_exit_immediately(status: c_int) -> ! { diff --git a/userspace/lib/ygglibc/src/ssp.rs b/userspace/lib/ygglibc/src/ssp.rs new file mode 100644 index 00000000..e632a37a --- /dev/null +++ b/userspace/lib/ygglibc/src/ssp.rs @@ -0,0 +1,15 @@ +#![allow(non_upper_case_globals)] + +#[no_mangle] +static mut __stack_chk_guard: usize = 0; + +#[no_mangle] +unsafe extern "C" fn __stack_chk_fail() -> ! { + panic!("!!! Stack smashing detected !!!") +} + +#[allow(static_mut_refs)] +pub unsafe fn init() { + // TODO generate random + __stack_chk_guard = 1234; +} diff --git a/userspace/lib/ygglibc/src/util.rs b/userspace/lib/ygglibc/src/util.rs index 8af24752..f61003af 100644 --- a/userspace/lib/ygglibc/src/util.rs +++ b/userspace/lib/ygglibc/src/util.rs @@ -1,14 +1,25 @@ -use core::ffi::{c_char, c_int, CStr}; +use core::{ + ffi::{c_char, c_int, CStr}, + fmt, + ptr::NonNull, +}; use yggdrasil_rt::io::RawFd; -use crate::{error::{EResult, TryFromExt}, headers::{errno, fcntl::AT_FDCWD}}; +use crate::{ + error::{EResult, TryFromExt}, + headers::{errno, fcntl::AT_FDCWD}, +}; pub trait PointerExt { unsafe fn ensure(self: *const Self) -> &'static Self; unsafe fn ensure_mut(self: *mut Self) -> &'static mut Self; - unsafe fn ensure_slice(self: *const Self, len: usize) -> &'static [Self] where Self: Sized; - unsafe fn ensure_slice_mut(self: *mut Self, len: usize) -> &'static mut [Self] where Self: Sized; + unsafe fn ensure_slice(self: *const Self, len: usize) -> &'static [Self] + where + Self: Sized; + unsafe fn ensure_slice_mut(self: *mut Self, len: usize) -> &'static mut [Self] + where + Self: Sized; } pub trait PointerStrExt { @@ -16,6 +27,12 @@ pub trait PointerStrExt { unsafe fn ensure_cstr(self: *const Self) -> &'static CStr; } +pub struct CStringWriter { + ptr: NonNull, + pos: usize, + limit: usize, +} + impl PointerExt for T { unsafe fn ensure(self: *const Self) -> &'static Self { unsafe { self.as_ref().expect("ensure() failed: NULL pointer") } @@ -25,14 +42,20 @@ impl PointerExt for T { unsafe { self.as_mut().expect("ensure_mut() failed: NULL pointer") } } - unsafe fn ensure_slice(self: *const Self, len: usize) -> &'static [Self] where Self: Sized { + unsafe fn ensure_slice(self: *const Self, len: usize) -> &'static [Self] + where + Self: Sized, + { if self.is_null() { panic!("ensure_slice() failed: NULL pointer"); } unsafe { core::slice::from_raw_parts(self, len) } } - unsafe fn ensure_slice_mut(self: *mut Self, len: usize) -> &'static mut [Self] where Self: Sized { + unsafe fn ensure_slice_mut(self: *mut Self, len: usize) -> &'static mut [Self] + where + Self: Sized, + { if self.is_null() { panic!("ensure_slice_mut() failed: NULL pointer"); } @@ -42,7 +65,9 @@ impl PointerExt for T { impl PointerStrExt for c_char { unsafe fn ensure_str(self: *const Self) -> &'static str { - self.ensure_cstr().to_str().expect("ensure_str() failed: not a valid string") + self.ensure_cstr() + .to_str() + .expect("ensure_str() failed: not a valid string") } unsafe fn ensure_cstr(self: *const Self) -> &'static CStr { @@ -53,6 +78,40 @@ impl PointerStrExt for c_char { } } +impl CStringWriter { + pub unsafe fn from_ptr(ptr: *mut c_char, limit: usize) -> Option { + let ptr = NonNull::new(ptr)?; + Some(Self { ptr, limit, pos: 0 }) + } + + pub fn finish(self) -> Option> { + if self.pos != self.limit { + unsafe { + self.ptr.add(self.pos).write(0); + } + Some(self.ptr) + } else { + None + } + } +} + +impl fmt::Write for CStringWriter { + fn write_str(&mut self, s: &str) -> fmt::Result { + if self.pos + s.len() < self.limit { + let dst = unsafe { + core::slice::from_raw_parts_mut(self.ptr.add(self.pos).cast().as_ptr(), s.len()) + }; + dst.copy_from_slice(s.as_bytes()); + self.pos += s.len(); + + Ok(()) + } else { + Err(fmt::Error) + } + } +} + pub fn at_fd(fd: c_int) -> EResult> { match fd { AT_FDCWD => EResult::Ok(None), @@ -60,3 +119,23 @@ pub fn at_fd(fd: c_int) -> EResult> { _ => EResult::Err(errno::EBADF), } } + +pub unsafe fn cstr_prefix bool>(a: NonNull, b: &[u8], cmp: F) -> bool { + for (i, &bch) in b.iter().enumerate() { + let ach = a.add(i).read(); + if ach == 0 || !cmp(ach, bch) { + return false; + } + } + true +} + +pub unsafe fn cstr_matches(a: NonNull, b: &[u8]) -> bool { + cstr_prefix(a, b, |a, b| a == b) +} + +pub unsafe fn cstr_matches_insensitive(a: NonNull, b: &[u8]) -> bool { + cstr_prefix(a, b, |a, b| { + a.to_ascii_lowercase() == b.to_ascii_lowercase() + }) +} diff --git a/xtask/src/build/cargo.rs b/xtask/src/build/cargo.rs index 6549637f..f474007d 100644 --- a/xtask/src/build/cargo.rs +++ b/xtask/src/build/cargo.rs @@ -80,7 +80,13 @@ impl<'e> CargoBuilder<'e> { } } Self::Ygglibc(env) => { - let target = format!("{:?}-unknown-none", env.arch); + let target = format!( + "{}/{:?}-unknown-none.json", + env.workspace_root + .join("userspace/lib/ygglibc/etc") + .display(), + env.arch + ); command .arg(arg) diff --git a/xtask/src/build/userspace.rs b/xtask/src/build/userspace.rs index 55a3a678..dc82fcd7 100644 --- a/xtask/src/build/userspace.rs +++ b/xtask/src/build/userspace.rs @@ -17,7 +17,9 @@ use crate::{ use super::{llvm::Llvm, InitrdGenerated}; pub struct Ygglibc { - lib_file: PathBuf, + static_lib_file: PathBuf, + shared_lib_file: PathBuf, + crt0_file: PathBuf, include_paths: Vec, } @@ -68,14 +70,23 @@ const PROGRAMS: &[(&str, &str)] = &[ ("dyn-loader", "libexec/dyn-loader"), // TODO: proper process for C program builds ("c-test", "c-test"), + ("c-test-static", "c-test-static"), ]; fn build_test_c_program(env: &BuildEnv, llvm: &Llvm) -> Result<(), Error> { log::info!("Building a test C program"); let target_dir = &env.userspace_output_dir; + let mut command = llvm.c_clang(env); command - .args(["-static", "-Og", "-ggdb"]) + .args([ + "-v", + "-Bdynamic", + "-fpie", + "-O0", + "-ggdb", + "-fstack-protector-strong", + ]) .arg("-o") .arg(target_dir.join("c-test")) .arg(env.workspace_root.join("test.c")); @@ -84,10 +95,21 @@ fn build_test_c_program(env: &BuildEnv, llvm: &Llvm) -> Result<(), Error> { return Err(Error::ExternalCommandFailed); } + let mut command = llvm.c_clang(env); + command + .args(["-v", "-static", "-O0", "-ggdb", "-fstack-protector-strong"]) + .arg("-o") + .arg(target_dir.join("c-test-static")) + .arg(env.workspace_root.join("test.c")); + + if !command.status()?.success() { + return Err(Error::ExternalCommandFailed); + } + Ok(()) } -fn install_ygglibc(env: &BuildEnv, ygglibc: Ygglibc) -> Result<(), Error> { +fn install_ygglibc(env: &BuildEnv, ygglibc: &Ygglibc) -> Result<(), Error> { log::info!("Installing ygglibc into LLVM sysroot"); let dst_lib_dir = env.llvm_sysroot.join("lib"); @@ -96,10 +118,12 @@ fn install_ygglibc(env: &BuildEnv, ygglibc: Ygglibc) -> Result<(), Error> { fs::create_dir_all(&dst_lib_dir)?; fs::create_dir_all(&dst_include_dir)?; - fs::copy(ygglibc.lib_file, dst_lib_dir.join("libygglibc.a"))?; + fs::copy(&ygglibc.static_lib_file, dst_lib_dir.join("libygglibc.a"))?; + fs::copy(&ygglibc.shared_lib_file, dst_lib_dir.join("libygglibc.so"))?; + fs::copy(&ygglibc.crt0_file, dst_lib_dir.join("crt0.o"))?; - for path in ygglibc.include_paths { - util::copy_dir_recursive(&path, &dst_include_dir)?; + for path in ygglibc.include_paths.iter() { + util::copy_dir_recursive(path, &dst_include_dir)?; } Ok(()) @@ -113,32 +137,38 @@ fn build_ygglibc(env: &BuildEnv) -> Result { env.profile.dir() )); CargoBuilder::Ygglibc(env).build(&ygglibc_dir)?; - let lib_file = target_dir.join("libygglibc.a"); + + let static_lib_file = target_dir.join("libygglibc.a"); + let shared_lib_file = target_dir.join("libygglibc.so"); + let crt0_file = target_dir.join("crt0.o"); let generated_includes = target_dir.join("include"); let static_includes = ygglibc_dir.join("include"); Ok(Ygglibc { - lib_file, + static_lib_file, + shared_lib_file, + crt0_file, include_paths: vec![generated_includes, static_includes], }) } -fn build_userspace(env: &BuildEnv, _: AllOk) -> Result<(), Error> { +fn build_userspace(env: &BuildEnv, _: AllOk) -> Result { log::info!("Building userspace"); CargoBuilder::Userspace(env).build(env.workspace_root.join("userspace"))?; CargoBuilder::Userspace(env).build(env.workspace_root.join("userspace/dynload-program"))?; let ygglibc = build_ygglibc(env)?; let llvm = llvm::build_llvm(env)?; - install_ygglibc(env, ygglibc)?; + install_ygglibc(env, &ygglibc)?; build_test_c_program(env, &llvm)?; - Ok(()) + Ok(ygglibc) } fn build_rootfs, D: AsRef>( env: &BuildEnv, install_extra: Vec<(PathBuf, PathBuf)>, + ygglibc: &Ygglibc, build_dir: S, rootfs_dir: D, _: AllOk, @@ -186,6 +216,13 @@ fn build_rootfs, D: AsRef>( env.arch.name() ), rootfs_dir.join("lib/libstd.so"))?; + log::info!("Installing ygglibc"); + + util::copy_file( + &ygglibc.shared_lib_file, + rootfs_dir.join("lib/libygglibc.so"), + )?; + log::info!("Installing extras"); for (src, dst) in install_extra { util::copy_file(src, rootfs_dir.join(dst))?; @@ -249,10 +286,11 @@ pub fn build_initrd( ) -> Result { let rootfs_dir = env.userspace_output_dir.join("rootfs"); - build_userspace(env, check)?; + let ygglibc = build_userspace(env, check)?; build_rootfs( env, install_extra, + &ygglibc, &env.userspace_output_dir, &rootfs_dir, check,