libc: dynamic libc
This commit is contained in:
parent
457e82d2e3
commit
312deb8a56
@ -289,13 +289,14 @@ pub fn clone_tls(space: &ProcessAddressSpace, image: &ProcessImage) -> Result<us
|
||||
fn is_dynamic<F: Read + Seek>(
|
||||
elf: &mut ElfStream<AnyEndian, FileReader<F>>,
|
||||
) -> Result<bool, Error> {
|
||||
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);
|
||||
};
|
||||
|
||||
|
@ -248,6 +248,7 @@ fn xxx_load_program<P: AsRef<Path>>(
|
||||
|
||||
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) => {
|
||||
|
8
test.c
8
test.c
@ -2,8 +2,12 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
int x = 1234;
|
||||
printf("Hello %d!!!\n", x);
|
||||
|
||||
int main() {
|
||||
printf("Hello!\n");
|
||||
return 0;
|
||||
}
|
||||
|
@ -94,6 +94,8 @@ fn run<P: AsRef<Path>>(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);
|
||||
|
@ -56,6 +56,7 @@ impl ObjectTls {
|
||||
impl Object {
|
||||
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ pub trait RelocationExt {
|
||||
tls_module_id: Option<usize>,
|
||||
image_symbol: F,
|
||||
image_base: usize,
|
||||
vma_start: usize,
|
||||
) -> Result<Option<RelValue>, Error>;
|
||||
}
|
||||
|
||||
@ -46,6 +47,7 @@ impl RelocationExt for Rela {
|
||||
tls_module_id: Option<usize>,
|
||||
image_symbol: F,
|
||||
image_base: usize,
|
||||
vma_start: usize,
|
||||
) -> Result<Option<RelValue>, 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<usize>,
|
||||
image_symbol: F,
|
||||
image_base: usize,
|
||||
vma_start: usize,
|
||||
) -> Result<Option<RelValue>, Error> {
|
||||
let image_base = image_base as i64;
|
||||
let symbol = image_symbol(self.r_sym)?;
|
||||
|
@ -51,6 +51,7 @@ impl State {
|
||||
load_base: usize,
|
||||
source: P,
|
||||
tls_index: Option<usize>,
|
||||
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 {
|
||||
|
25
userspace/lib/ygglibc/Cargo.lock
generated
25
userspace/lib/ygglibc/Cargo.lock
generated
@ -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",
|
||||
|
@ -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" }
|
||||
|
@ -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<Path>, header_output: impl AsRef<Path
|
||||
.unwrap()
|
||||
.replace("_", "/");
|
||||
// TODO use outer workspace's target directory?
|
||||
let header_path = header_output
|
||||
.join(&relative_path)
|
||||
.with_extension("h");
|
||||
let header_path = header_output.join(&relative_path).with_extension("h");
|
||||
let mod_path = config_path.with_file_name("mod.rs");
|
||||
|
||||
let mut config = cbindgen::Config::from_file(config_path).unwrap();
|
||||
@ -52,11 +53,32 @@ fn generate_header(config_path: impl AsRef<Path>, header_output: impl AsRef<Path
|
||||
.write_to_file(header_path);
|
||||
}
|
||||
|
||||
fn compile_crt0(arch: &str, output_dir: impl AsRef<Path>) {
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
|
11
userspace/lib/ygglibc/crt/x86_64/crt0.S
Normal file
11
userspace/lib/ygglibc/crt/x86_64/crt0.S
Normal file
@ -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
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
16
userspace/lib/ygglibc/include/bits/sys/stat.h
Normal file
16
userspace/lib/ygglibc/include/bits/sys/stat.h
Normal file
@ -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
|
6
userspace/lib/ygglibc/include/bits/time.h
Normal file
6
userspace/lib/ygglibc/include/bits/time.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef _YGGDRASIL_TIME_H
|
||||
#define _YGGDRASIL_TIME_H 1
|
||||
|
||||
typedef struct timespec __ygg_timespec_t;
|
||||
|
||||
#endif
|
@ -31,6 +31,10 @@ pub trait ResultExt<T, E> {
|
||||
fn e_map_err<F: FnOnce(E) -> Errno>(self, map: F) -> EResult<T>;
|
||||
}
|
||||
|
||||
pub trait OptionExt<T> {
|
||||
fn e_ok_or(self, err: Errno) -> EResult<T>;
|
||||
}
|
||||
|
||||
pub trait CResult {
|
||||
const ERROR: Self;
|
||||
}
|
||||
@ -144,6 +148,15 @@ impl<T, E> ResultExt<T, E> for Result<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> OptionExt<T> for Option<T> {
|
||||
fn e_ok_or(self, err: Errno) -> EResult<T> {
|
||||
match self {
|
||||
Some(value) => EResult::Ok(value),
|
||||
None => EResult::Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Try for EResult<T> {
|
||||
type Output = T;
|
||||
type Residual = EResult<Infallible>;
|
||||
|
@ -1,4 +1,4 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_camel_case_types, non_upper_case_globals)]
|
||||
|
||||
// <aio.h> -
|
||||
// <arpa/inet.h> -
|
||||
@ -60,11 +60,11 @@
|
||||
// <sys/sem.h> -
|
||||
// <sys/shm.h> -
|
||||
// <sys/socket.h> -
|
||||
// <sys/stat.h> -
|
||||
// <sys/stat.h> =
|
||||
// <sys/statvfs.h> -
|
||||
// <sys/time.h> -
|
||||
// <sys/time.h> =
|
||||
// <sys/times.h> -
|
||||
// <sys/types.h> -
|
||||
// <sys/types.h> +
|
||||
// <sys/uio.h> -
|
||||
// <sys/un.h> -
|
||||
// <sys/utsname.h> -
|
||||
@ -73,7 +73,7 @@
|
||||
// <tar.h> -
|
||||
// <termios.h> -
|
||||
// <tgmath.h> -
|
||||
// <time.h> -
|
||||
// <time.h> =
|
||||
// <trace.h> -
|
||||
// <ulimit.h> -
|
||||
// <unistd.h> ~
|
||||
@ -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;
|
||||
|
416
userspace/lib/ygglibc/src/headers/stdlib/conv.rs
Normal file
416
userspace/lib/ygglibc/src/headers/stdlib/conv.rs
Normal file
@ -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<Self> {
|
||||
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<I: StrToInt>(
|
||||
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<U: StrToUint>(
|
||||
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<I: StrToInt>(mut src: NonNull<u8>, radix: Option<Radix>) -> (I, NonNull<u8>) {
|
||||
// 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::<I::Unsigned>(src, radix, false);
|
||||
|
||||
(I::from_unsigned(unsigned, sign), endptr)
|
||||
}
|
||||
|
||||
unsafe fn str_to_uint<U: StrToUint>(
|
||||
mut src: NonNull<u8>,
|
||||
radix: Option<Radix>,
|
||||
skip_whitespace: bool,
|
||||
) -> (U, NonNull<u8>) {
|
||||
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<u8>) -> (f64, NonNull<u8>) {
|
||||
// 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<u8>, sign: bool) -> (f64, NonNull<u8>) {
|
||||
// 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<u8>) -> bool {
|
||||
cstr_matches_insensitive(s, b"nan")
|
||||
}
|
||||
|
||||
unsafe fn is_inf(s: NonNull<u8>) -> Option<usize> {
|
||||
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<u8>) -> 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,
|
||||
}
|
||||
}
|
8
userspace/lib/ygglibc/src/headers/stdlib/io.rs
Normal file
8
userspace/lib/ygglibc/src/headers/stdlib/io.rs
Normal file
@ -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);
|
47
userspace/lib/ygglibc/src/headers/stdlib/malloc.rs
Normal file
47
userspace/lib/ygglibc/src/headers/stdlib/malloc.rs
Normal file
@ -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<c_void> {
|
||||
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<c_void> {
|
||||
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<c_void> {
|
||||
let old_ptr = NonNull::new(ptr);
|
||||
let new_ptr = allocator::c_realloc(old_ptr, size)?;
|
||||
CPtrResult::success(new_ptr)
|
||||
}
|
61
userspace/lib/ygglibc/src/headers/stdlib/math.rs
Normal file
61
userspace/lib/ygglibc/src/headers/stdlib/math.rs
Normal file
@ -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
|
||||
}
|
||||
}
|
@ -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<c_void> {
|
||||
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
|
||||
|
61
userspace/lib/ygglibc/src/headers/stdlib/process.rs
Normal file
61
userspace/lib/ygglibc/src/headers/stdlib/process.rs
Normal file
@ -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<c_char> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn putenv(_value: *const c_char) -> CPtrResult<c_char> {
|
||||
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!()
|
||||
}
|
17
userspace/lib/ygglibc/src/headers/stdlib/random.rs
Normal file
17
userspace/lib/ygglibc/src/headers/stdlib/random.rs
Normal file
@ -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);
|
134
userspace/lib/ygglibc/src/headers/stdlib/util.rs
Normal file
134
userspace/lib/ygglibc/src/headers/stdlib/util.rs
Normal file
@ -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<Self> {
|
||||
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<F: Fn(*mut c_void, *mut c_void) -> 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<F: Fn(*mut c_void, *mut c_void) -> 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<F: Fn(*mut c_void, *mut c_void) -> 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 **);
|
6
userspace/lib/ygglibc/src/headers/stdlib/wide.rs
Normal file
6
userspace/lib/ygglibc/src/headers/stdlib/wide.rs
Normal file
@ -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);
|
19
userspace/lib/ygglibc/src/headers/sys_stat/cbindgen.toml
Normal file
19
userspace/lib/ygglibc/src/headers/sys_stat/cbindgen.toml
Normal file
@ -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"]
|
221
userspace/lib/ygglibc/src/headers/sys_stat/mod.rs
Normal file
221
userspace/lib/ygglibc/src/headers/sys_stat/mod.rs
Normal file
@ -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<FileAttr> 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!()
|
||||
}
|
14
userspace/lib/ygglibc/src/headers/sys_time/cbindgen.toml
Normal file
14
userspace/lib/ygglibc/src/headers/sys_time/cbindgen.toml
Normal file
@ -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 = []
|
20
userspace/lib/ygglibc/src/headers/sys_time/mod.rs
Normal file
20
userspace/lib/ygglibc/src/headers/sys_time/mod.rs
Normal file
@ -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;
|
@ -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);
|
||||
}
|
||||
|
20
userspace/lib/ygglibc/src/headers/time/cbindgen.toml
Normal file
20
userspace/lib/ygglibc/src/headers/time/cbindgen.toml
Normal file
@ -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"]
|
64
userspace/lib/ygglibc/src/headers/time/convert.rs
Normal file
64
userspace/lib/ygglibc/src/headers/time/convert.rs
Normal file
@ -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<tm> = 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<tm> = 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,
|
||||
}
|
||||
}
|
113
userspace/lib/ygglibc/src/headers/time/mod.rs
Normal file
113
userspace/lib/ygglibc/src/headers/time/mod.rs
Normal file
@ -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<NaiveDateTime>;
|
||||
|
||||
fn to_datetime<Tz: TimeZone>(&self, tz: Tz) -> Option<DateTime<Tz>> {
|
||||
self.to_naive_datetime()?.and_local_timezone(tz).latest()
|
||||
}
|
||||
}
|
||||
|
||||
impl tm {
|
||||
pub fn naive_date(&self) -> Option<NaiveDate> {
|
||||
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> {
|
||||
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<NaiveDateTime> {
|
||||
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> {
|
||||
NaiveDateTime::from_timestamp_opt(self.0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Datelike + Timelike> From<T> 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
|
||||
}
|
112
userspace/lib/ygglibc/src/headers/time/string.rs
Normal file
112
userspace/lib/ygglibc/src/headers/time/string.rs
Normal file
@ -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<Tz: TimeZone, T: CDateTime>(
|
||||
t: &T,
|
||||
tz: Tz,
|
||||
buffer: *mut c_char,
|
||||
) -> Option<NonNull<c_char>> {
|
||||
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!()
|
||||
}
|
55
userspace/lib/ygglibc/src/headers/time/timer.rs
Normal file
55
userspace/lib/ygglibc/src/headers/time/timer.rs
Normal file
@ -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
|
39
userspace/lib/ygglibc/src/headers/time/util.rs
Normal file
39
userspace/lib/ygglibc/src/headers/time/util.rs
Normal file
@ -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!()
|
||||
}
|
@ -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!()
|
||||
}
|
||||
}
|
||||
|
8
userspace/lib/ygglibc/src/panic.rs
Normal file
8
userspace/lib/ygglibc/src/panic.rs
Normal file
@ -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();
|
||||
}
|
@ -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) -> ! {
|
||||
|
15
userspace/lib/ygglibc/src/ssp.rs
Normal file
15
userspace/lib/ygglibc/src/ssp.rs
Normal file
@ -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;
|
||||
}
|
@ -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<c_char>,
|
||||
pos: usize,
|
||||
limit: usize,
|
||||
}
|
||||
|
||||
impl<T> 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<T> 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<T> 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<Self> {
|
||||
let ptr = NonNull::new(ptr)?;
|
||||
Some(Self { ptr, limit, pos: 0 })
|
||||
}
|
||||
|
||||
pub fn finish(self) -> Option<NonNull<c_char>> {
|
||||
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<Option<RawFd>> {
|
||||
match fd {
|
||||
AT_FDCWD => EResult::Ok(None),
|
||||
@ -60,3 +119,23 @@ pub fn at_fd(fd: c_int) -> EResult<Option<RawFd>> {
|
||||
_ => EResult::Err(errno::EBADF),
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn cstr_prefix<F: Fn(u8, u8) -> bool>(a: NonNull<u8>, 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<u8>, b: &[u8]) -> bool {
|
||||
cstr_prefix(a, b, |a, b| a == b)
|
||||
}
|
||||
|
||||
pub unsafe fn cstr_matches_insensitive(a: NonNull<u8>, b: &[u8]) -> bool {
|
||||
cstr_prefix(a, b, |a, b| {
|
||||
a.to_ascii_lowercase() == b.to_ascii_lowercase()
|
||||
})
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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<PathBuf>,
|
||||
}
|
||||
|
||||
@ -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<Ygglibc, Error> {
|
||||
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<Ygglibc, Error> {
|
||||
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<S: AsRef<Path>, D: AsRef<Path>>(
|
||||
env: &BuildEnv,
|
||||
install_extra: Vec<(PathBuf, PathBuf)>,
|
||||
ygglibc: &Ygglibc,
|
||||
build_dir: S,
|
||||
rootfs_dir: D,
|
||||
_: AllOk,
|
||||
@ -186,6 +216,13 @@ fn build_rootfs<S: AsRef<Path>, D: AsRef<Path>>(
|
||||
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<InitrdGenerated, Error> {
|
||||
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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user