libc: basic ygglibc with stubs

This commit is contained in:
Mark Poliakov 2024-11-11 15:19:36 +02:00
parent cdf9243962
commit e0600c2bf6
53 changed files with 4976 additions and 5 deletions

View File

@ -6,6 +6,7 @@ exclude = [
"tool/abi-generator",
"toolchain",
"userspace/dynload-program",
"userspace/lib/ygglibc"
]
members = [
"xtask",

View File

@ -535,7 +535,7 @@ fn write_rela(rela: &Rela, space: &ProcessAddressSpace, b: usize) -> Result<(),
match width {
8 => {
unsafe { (dst.as_mut_ptr() as *mut u64).write_volatile(value as u64) };
unsafe { (dst.as_mut_ptr() as *mut u64).write_unaligned(value as u64) };
Ok(())
}
_ => todo!("Unhandled rela width: {}", width),

9
test.c Normal file
View File

@ -0,0 +1,9 @@
#include <unistd.h>
#include <stdio.h>
int main() {
char buf[512];
int v = 1234 + 3;
snprintf(buf, sizeof(buf), "Hello %d\n", v);
return 0;
}

View File

@ -17,9 +17,8 @@ members = [
"lib/yasync",
"rsh",
"lib/cross",
"crypt"
]
exclude = ["dynload-program", "test-kernel-module"]
"crypt"]
exclude = ["dynload-program", "test-kernel-module", "lib/ygglibc"]
[workspace.dependencies]
log = "0.4.22"

1
userspace/lib/ygglibc/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

576
userspace/lib/ygglibc/Cargo.lock generated Normal file
View File

@ -0,0 +1,576 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "abi-generator"
version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
"thiserror",
]
[[package]]
name = "abi-lib"
version = "0.1.0"
[[package]]
name = "anstream"
version = "0.6.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
[[package]]
name = "anstyle-parse"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
dependencies = [
"anstyle",
"windows-sys 0.59.0",
]
[[package]]
name = "bitflags"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "bytemuck"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d"
dependencies = [
"bytemuck_derive",
]
[[package]]
name = "bytemuck_derive"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
]
[[package]]
name = "cbindgen"
version = "0.26.0"
source = "git+https://git.alnyan.me/yggdrasil/cbindgen.git?branch=master#97f9e1961ab890c7d0f760793abd27b048982b74"
dependencies = [
"clap",
"heck",
"indexmap",
"log",
"proc-macro2",
"quote",
"serde",
"serde_json",
"syn 1.0.109",
"tempfile",
"toml",
]
[[package]]
name = "cc"
version = "1.1.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baee610e9452a8f6f0a1b6194ec09ff9e2d85dea54432acdae41aa0761c95d70"
dependencies = [
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "4.5.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
version = "4.5.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_lex"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
[[package]]
name = "colorchoice"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "errno"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "fastrand"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
[[package]]
name = "hashbrown"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3"
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "indexmap"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itoa"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "libc"
version = "0.2.161"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
[[package]]
name = "libyalloc"
version = "0.1.0"
dependencies = [
"libc",
"yggdrasil-rt",
]
[[package]]
name = "linux-raw-sys"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]]
name = "log"
version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "once_cell"
version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]]
name = "prettyplease"
version = "0.2.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033"
dependencies = [
"proc-macro2",
"syn 2.0.87",
]
[[package]]
name = "proc-macro2"
version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rustix"
version = "0.38.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "375116bee2be9ed569afe2154ea6a99dfdffd257f533f187498c2a8f5feaf4ee"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.52.0",
]
[[package]]
name = "ryu"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "serde"
version = "1.0.214"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.214"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
]
[[package]]
name = "serde_json"
version = "1.0.132"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
[[package]]
name = "serde_spanned"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
dependencies = [
"serde",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tempfile"
version = "3.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
dependencies = [
"cfg-if",
"fastrand",
"once_cell",
"rustix",
"windows-sys 0.59.0",
]
[[package]]
name = "thiserror"
version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02dd99dc800bbb97186339685293e1cc5d9df1f8fae2d0aecd9ff1c77efea892"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7c61ec9a6f64d2793d8a45faba21efbe3ced62a886d44c36a009b2b519b4c7e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.87",
]
[[package]]
name = "toml"
version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.22.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
]
[[package]]
name = "unicode-ident"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "0.6.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
dependencies = [
"memchr",
]
[[package]]
name = "yggdrasil-abi"
version = "0.1.0"
dependencies = [
"abi-generator",
"abi-lib",
"bytemuck",
"prettyplease",
]
[[package]]
name = "yggdrasil-rt"
version = "0.1.0"
dependencies = [
"abi-generator",
"abi-lib",
"cc",
"prettyplease",
"yggdrasil-abi",
]
[[package]]
name = "ygglibc"
version = "0.1.0"
dependencies = [
"bitflags",
"cbindgen",
"libyalloc",
"yggdrasil-abi",
"yggdrasil-rt",
]

View File

@ -0,0 +1,16 @@
[package]
name = "ygglibc"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["staticlib"]
[dependencies]
yggdrasil-rt = { path = "../../../lib/runtime" }
yggdrasil-abi = { path = "../../../lib/abi", features = ["alloc", "bytemuck"] }
libyalloc = { path = "../../../lib/libyalloc" }
bitflags = "2.6.0"
[build-dependencies]
cbindgen = { git = "https://git.alnyan.me/yggdrasil/cbindgen.git", branch = "master" }

View File

@ -0,0 +1,75 @@
use std::{
env, fs::{self, DirEntry}, path::Path
};
const RENAMES: &[(&str, &str)] = &[
("CUsizeResult", "size_t"),
("CIsizeResult", "ssize_t"),
("CEofResult", "int"),
("CIntCountResult", "int"),
("CIntZeroResult", "int"),
("CFdResult", "int"),
("COffsetResult", "off_t"),
("CPidResult", "pid_t"),
];
fn include_dir(d: &DirEntry) -> bool {
d.metadata().map(|m| m.is_dir()).unwrap_or(false)
&& d.path()
.iter()
.nth(2)
.map_or(false, |c| c.to_str().map_or(false, |x| !x.starts_with("_")))
}
fn generate_header(config_path: impl AsRef<Path>, header_output: impl AsRef<Path>) {
let config_path = config_path.as_ref();
let header_output = header_output.as_ref();
let relative_path = config_path
.strip_prefix("src/headers")
.ok()
.and_then(|p| p.parent())
.and_then(|p| p.to_str())
.unwrap()
.replace("_", "/");
// TODO use outer workspace's target directory?
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();
config
.export
.rename
.extend(RENAMES.into_iter().map(|&(x, y)| (x.into(), y.into())));
cbindgen::Builder::new()
.with_config(config)
.with_src(mod_path)
.generate()
.unwrap()
.write_to_file(header_path);
}
fn main() {
let target = env::var("TARGET").expect("$TARGET is not set");
let profile = env::var("PROFILE").expect("$PROFILE is not set");
let header_output = format!("target/{target}/{profile}/include");
fs::read_dir("src/headers")
.unwrap()
.into_iter()
.filter_map(Result::ok)
.filter(|d| include_dir(d))
.map(|d| d.path().as_path().join("cbindgen.toml"))
.filter(|p| p.exists())
.for_each(|p| {
println!("cargo:rerun-if-changed={:?}", p.parent().unwrap());
println!("cargo:rerun-if-changed={:?}", p);
println!("cargo:rerun-if-changed={:?}", p.with_file_name("mod.rs"));
generate_header(&p, &header_output);
});
}

View File

@ -0,0 +1,23 @@
{
"is-builtin": false,
"arch": "x86_64",
"cpu": "x86-64",
"os": "none",
"llvm-target": "x86_64-unknown-linux-gnu",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"max-atomic-width": 64,
"target-pointer-width": "64",
"features": "-avx,-sse,+soft-float",
"disable-redzone": true,
"executables": true,
"panic-strategy": "abort",
"dynamic-linking": true,
"relocation-model": "static",
"position-independent-executables": false,
"has-thread-local": false,
"linker": "rust-lld",
"linker-flavor": "ld.lld"
}

View File

@ -0,0 +1,6 @@
#ifndef _YGGDRASIL_ERRNO_H
#define _YGGDRASIL_ERRNO_H 1
extern int errno;
#endif

View File

@ -0,0 +1,17 @@
#ifndef _YGGDRASIL_STDIO_H
#define _YGGDRASIL_STDIO_H 1
struct __FILE;
typedef struct __FILE FILE;
extern FILE *stdin;
extern FILE *stdout;
extern FILE *stderr;
int dprintf(int fd, const char *fmt, ...);
int fprintf(FILE *fp, const char *fmt, ...);
int printf(const char *fmt, ...);
int snprintf(char *buf, size_t len, const char *fmt, ...);
int sprintf(char *buf, const char *fmt, ...);
#endif

View File

@ -0,0 +1,8 @@
#ifndef _YGGDRASIL_UNISTD_H
#define _YGGDRASIL_UNISTD_H 1
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ...);
#endif

View File

@ -0,0 +1,89 @@
use core::{
alloc::{GlobalAlloc, Layout}, ffi::c_void, ptr::{self, null, null_mut, NonNull}
};
use libyalloc::{allocator::BucketAllocator, sys::PageProvider};
use yggdrasil_rt::{mem::MappingSource, sys as syscall};
use crate::error::EResult;
struct Allocator;
struct PageProviderImpl;
unsafe impl GlobalAlloc for Allocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let pointer = YALLOC.allocate(layout);
match pointer {
Some(ptr) => ptr.as_ptr(),
None => null_mut(),
}
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
let pointer = NonNull::new(ptr).expect("NULL pointer passed to dealloc()");
YALLOC.free(pointer, layout);
}
}
impl PageProvider for PageProviderImpl {
fn map_pages(count: usize) -> Option<NonNull<u8>> {
match unsafe { syscall::map_memory(None, count * 0x1000, &MappingSource::Anonymous) } {
Ok(address) => {
let pointer = ptr::with_exposed_provenance_mut(address);
NonNull::new(pointer)
}
Err(_error) => None,
}
}
fn unmap_pages(address: NonNull<u8>, count: usize) {
let address = address.as_ptr().addr();
unsafe { syscall::unmap_memory(address, count * 0x1000) }
.expect("Could not unmap memory pages");
}
}
const MALLOC_HEADER_SIZE: usize = size_of::<usize>();
fn c_alloc(size: usize, mut align: usize, zero: bool) -> EResult<NonNull<c_void>> {
// assert!(align.is_power_of_two());
// if align < align_of::<usize>() {
// align = align_of::<usize>();
// }
// let offset = (MALLOC_HEADER_SIZE + align - 1) & !(align - 1);
// let size = offset + size;
// let layout = Layout::from_size_align(size, align).ok()?;
// let ptr = unsafe { YALLOC.allocate(layout) }?;
// if zero {
// // TODO
// }
// unsafe {
// // Write the size right below the pointer
// ptr.add(offset - size_of::<usize>()).cast::<usize>().write(size);
// }
todo!()
// unsafe { Some(ptr.cast::<c_void>().add(offset)) }
}
pub unsafe fn malloc(size: usize) -> EResult<NonNull<c_void>> {
todo!()
// // TODO errno setting
// match c_alloc(size, 16, false) {
// Some(ptr) => ptr.as_ptr(),
// None => null_mut()
// }
}
pub unsafe fn free(ptr: NonNull<c_void>) {
todo!()
}
#[global_allocator]
static ALLOCATOR: Allocator = Allocator;
// TODO locking for multithreaded binaries
static mut YALLOC: BucketAllocator<PageProviderImpl> = BucketAllocator::new();

View File

@ -0,0 +1,9 @@
use alloc::{string::String, vec::Vec};
use yggdrasil_rt::process::ProgramArgumentInner;
pub fn handle_kernel_argument(arg: usize) -> (Vec<String>, Vec<String>) {
let arg_ptr: *const ProgramArgumentInner = core::ptr::with_exposed_provenance(arg);
let arg = unsafe { arg_ptr.as_ref() }.expect("TODO");
todo!()
}

View File

@ -0,0 +1,260 @@
use core::{
convert::Infallible,
ffi::c_int,
ops::{ControlFlow, FromResidual, Try},
ptr::{null_mut, NonNull},
};
use yggdrasil_rt::io::RawFd;
use crate::headers::{errno::Errno, sys_types::off_t};
macro impl_from_residual($($ty:ty),+) {
$(
impl FromResidual<EResult<Infallible>> for $ty {
fn from_residual(residual: EResult<Infallible>) -> Self {
let err = residual.unwrap_err();
unsafe {
errno = err;
}
Self::ERROR
}
}
)+
}
#[no_mangle]
#[allow(non_upper_case_globals)]
pub static mut errno: Errno = Errno(0);
pub trait CResult {
const ERROR: Self;
}
#[must_use = "EResult must be converted into its output type with proper errno set"]
pub enum EResult<T> {
Ok(T),
Err(Errno),
}
/// On success: non-null ptr
/// On failure: NULL
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct CPtrResult<T>(*mut T);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct CIsizeResult(isize);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct CIntZeroResult(c_int);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct CEofResult(c_int);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct CIntCountResult(c_int);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct CFdResult(c_int);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct COffsetResult(off_t);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct CUsizeResult(usize);
pub trait TryFromExt<T>: Sized {
fn e_try_from(value: T) -> EResult<Self>;
}
impl<T> EResult<T> {
// pub fn is_ok(&self) -> bool {
// matches!(self, Self::Ok(_))
// }
pub fn is_err(&self) -> bool {
matches!(self, Self::Err(_))
}
pub fn ok(self) -> Option<T> {
match self {
Self::Ok(res) => Some(res),
Self::Err(_) => None,
}
}
pub fn expect(self, message: &str) -> T {
match self {
Self::Ok(value) => value,
Self::Err(err) => panic!("expect() failed: {}, errno={:?}", message, err)
}
}
pub fn unwrap_err(self) -> Errno {
match self {
Self::Ok(_) => panic!("Expected an error"),
Self::Err(err) => err,
}
}
pub fn into_result<E, F: FnOnce(Errno) -> E>(
self,
map_err: F,
set_errno: bool,
) -> Result<T, E> {
match self {
Self::Ok(value) => Ok(value),
Self::Err(err) => {
if set_errno {
unsafe {
errno = err;
}
}
Err(map_err(err))
}
}
}
}
impl<T> Try for EResult<T> {
type Output = T;
type Residual = EResult<Infallible>;
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
match self {
Self::Ok(value) => ControlFlow::Continue(value),
Self::Err(err) => ControlFlow::Break(EResult::Err(err)),
}
}
fn from_output(output: Self::Output) -> Self {
Self::Ok(output)
}
}
impl<T> FromResidual<EResult<Infallible>> for EResult<T> {
fn from_residual(residual: EResult<Infallible>) -> Self {
let err = residual.unwrap_err();
Self::Err(err)
}
}
impl<T, E: Into<Errno>> FromResidual<Result<Infallible, E>> for EResult<T> {
fn from_residual(residual: Result<Infallible, E>) -> Self {
let err = residual.unwrap_err();
Self::Err(err.into())
}
}
impl<T, E: From<Errno>> FromResidual<EResult<Infallible>> for Result<T, E> {
fn from_residual(residual: EResult<Infallible>) -> Self {
let err = residual.unwrap_err();
Self::Err(err.into())
}
}
impl<T> CPtrResult<T> {
pub const fn success(ptr: NonNull<T>) -> Self {
Self(ptr.as_ptr())
}
}
impl<T> CResult for CPtrResult<T> {
const ERROR: Self = Self(null_mut());
}
impl<T> FromResidual<EResult<Infallible>> for CPtrResult<T> {
fn from_residual(residual: EResult<Infallible>) -> Self {
let err = residual.unwrap_err();
unsafe {
errno = err;
}
Self::ERROR
}
}
impl CIsizeResult {
pub const fn success(value: usize) -> Self {
Self(value as _)
}
}
impl CResult for CIsizeResult {
const ERROR: Self = Self(-1);
}
impl CIntZeroResult {
pub const SUCCESS: Self = Self(0);
}
impl CResult for CIntZeroResult {
const ERROR: Self = Self(-1);
}
impl CEofResult {
pub fn success(value: c_int) -> Self {
Self(value)
}
}
impl CResult for CEofResult {
const ERROR: Self = Self(-1);
}
impl CIntCountResult {
pub fn success(value: c_int) -> Self {
Self(value)
}
}
impl CResult for CIntCountResult {
const ERROR: Self = Self(-1);
}
impl CFdResult {
pub fn success(value: RawFd) -> Self {
Self(value.into_raw().try_into().unwrap())
}
}
impl CResult for CFdResult {
const ERROR: Self = Self(-1);
}
impl COffsetResult {
pub fn success(value: u64) -> Self {
Self(value.try_into().unwrap())
}
}
impl CResult for COffsetResult {
const ERROR: Self = Self(-1);
}
impl CUsizeResult {
pub fn success(value: usize) -> Self {
Self(value)
}
}
impl CResult for CUsizeResult {
const ERROR: Self = Self(0);
}
impl_from_residual!(
CIsizeResult,
CIntZeroResult,
CEofResult,
CIntCountResult,
CFdResult,
COffsetResult,
CUsizeResult
);

View File

@ -0,0 +1,10 @@
language = "C"
style = "Type"
sys_includes = []
no_includes = true
include_guard = "_ERRNO_H"
trailer = "#include <bits/errno.h>"
[export]

View File

@ -0,0 +1,188 @@
use core::ffi::{c_char, c_int, CStr};
macro_rules! static_cstr {
($string:expr) => {
unsafe {
::core::ffi::CStr::from_bytes_with_nul_unchecked(concat!($string, "\0").as_bytes())
}
};
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
#[repr(transparent)]
pub struct Errno(pub c_int);
pub const ESUCCESS: Errno = Errno(0);
pub const EPERM: Errno = Errno(1);
pub const ENOENT: Errno = Errno(2);
pub const ESRCH: Errno = Errno(3);
pub const EINTR: Errno = Errno(4);
pub const EIO: Errno = Errno(5);
pub const ENXIO: Errno = Errno(6);
pub const E2BIG: Errno = Errno(7);
pub const ENOEXEC: Errno = Errno(8);
pub const EBADF: Errno = Errno(9);
pub const ECHILD: Errno = Errno(10);
pub const EAGAIN: Errno = Errno(11);
pub const ENOMEM: Errno = Errno(12);
pub const EACCES: Errno = Errno(13);
pub const EFAULT: Errno = Errno(14);
pub const ENOTBLK: Errno = Errno(15);
pub const EBUSY: Errno = Errno(16);
pub const EEXIST: Errno = Errno(17);
pub const EXDEV: Errno = Errno(18);
pub const ENODEV: Errno = Errno(19);
pub const ENOTDIR: Errno = Errno(20);
pub const EISDIR: Errno = Errno(21);
pub const EINVAL: Errno = Errno(22);
pub const ENFILE: Errno = Errno(23);
pub const EMFILE: Errno = Errno(24);
pub const ENOTTY: Errno = Errno(25);
pub const ETXTBSY: Errno = Errno(26);
pub const EFBIG: Errno = Errno(27);
pub const ENOSPC: Errno = Errno(28);
pub const ESPIPE: Errno = Errno(29);
pub const EROFS: Errno = Errno(30);
pub const EMLINK: Errno = Errno(31);
pub const EPIPE: Errno = Errno(32);
pub const EDOM: Errno = Errno(33);
pub const ERANGE: Errno = Errno(34);
pub const EDEADLK: Errno = Errno(35);
pub const ENAMETOOLONG: Errno = Errno(36);
pub const ENOLCK: Errno = Errno(37);
pub const ENOSYS: Errno = Errno(38);
pub const ENOTEMPTY: Errno = Errno(39);
pub const ELOOP: Errno = Errno(40);
pub const EWOULDBLOCK: Errno = EAGAIN;
pub const ENOMSG: Errno = Errno(42);
pub const MAX_ERROR: Errno = ERANGE;
static SUCCESS: &CStr = static_cstr!("Success");
pub static UNKNOWN_ERROR: &CStr = static_cstr!("Unknown error");
static ERRNO_STRINGS: &[&CStr] = &[
// 0
SUCCESS,
// EPERM 1
static_cstr!("Operation not permitted"),
// ENOENT 2
static_cstr!("No such file or directory"),
// ESRCH 3
static_cstr!("No such process"),
// EINTR 4
static_cstr!("Interrupted system call"),
// EIO 5
static_cstr!("Input/output error"),
// ENXIO 6
static_cstr!("No such device or address"),
// E2BIG 7
static_cstr!("Argument list too long"),
// ENOEXEC 8
static_cstr!("Exec format error"),
// EBADF 9
static_cstr!("Bad file descriptor"),
// ECHILD 10
static_cstr!("No child processses"),
// EAGAIN 11
static_cstr!("Resource temporarily unavailable"),
// ENOMEM 12
static_cstr!("Cannot allocate memory"),
// EACCES 13
static_cstr!("Permission denied"),
// EFAULT 14
static_cstr!("Bad address"),
// ENOTBLK 15
static_cstr!("Block device required"),
// EBUSY 16
static_cstr!("Device or resource busy"),
// EEXIST 17
static_cstr!("File exists"),
// EXDEV 18
static_cstr!("Invalid cross-device link"),
// ENODEV 19
static_cstr!("No such device"),
// ENOTDIR 20
static_cstr!("Not a directory"),
// EISDIR 21
static_cstr!("Is a directory"),
// EINVAL 22
static_cstr!("Invalid argument"),
// ENFILE 23
static_cstr!("Too many open files in system"),
// EMFILE 24
static_cstr!("Too many open files"),
// ENOTTY 25
static_cstr!("Inappropriate ioctl for device"),
// ETXTBSY 26
static_cstr!("Text file busy"),
// EFBIG 27
static_cstr!("File too large"),
// ENOSPC 28
static_cstr!("No space left on device"),
// ESPIPE 29
static_cstr!("Illegal seek"),
// EROFS 30
static_cstr!("Read-only file system"),
// EMLINK 31
static_cstr!("Too many links"),
// EPIPE 32
static_cstr!("Broken pipe"),
// EDOM 33
static_cstr!("Numerical argument out of domain"),
// ERANGE 34
static_cstr!("Numerical result out of range"),
];
impl Errno {
pub fn to_c_str(&self) -> *const c_char {
ERRNO_STRINGS
.get(self.0 as usize)
.copied()
.unwrap_or(SUCCESS)
.as_ptr() as *const _
}
pub fn from_c_int(v: c_int) -> Option<Self> {
if v < 0 || v > MAX_ERROR.0 {
None
} else {
Some(Self(v))
}
}
}
impl From<yggdrasil_rt::Error> for Errno {
fn from(value: yggdrasil_rt::Error) -> Self {
use yggdrasil_rt::Error as E;
match value {
E::TimedOut => todo!(),
E::MissingData => todo!(),
// TODO ???
E::InvalidMemoryOperation => EPERM,
// TODO ???
E::NotImplemented => EPERM,
// TODO ???
E::DirectoryNotEmpty => EISDIR,
// TODO ???
E::WouldBlock => EAGAIN,
// TODO ???
E::InvalidOperation => EPERM,
E::DoesNotExist => ENOENT,
E::AlreadyExists => EEXIST,
E::InvalidArgument => EINVAL,
E::NotADirectory => ENOTDIR,
E::PermissionDenied => EACCES,
E::UnrecognizedExecutable => ENOEXEC,
E::ReadOnly => EROFS,
E::OutOfMemory => ENOMEM,
E::InvalidFile => EBADF,
E::Interrupted => EINTR,
E::IsADirectory => EISDIR,
_ => todo!()
}
}
}

View File

@ -0,0 +1,8 @@
#![allow(non_camel_case_types)]
pub mod errno;
pub mod stdio;
pub mod stdlib;
pub mod unistd;
pub mod sys_types;

View File

@ -0,0 +1,31 @@
language = "C"
style = "Type"
sys_includes = [
"stdarg.h",
"stddef.h",
"stdint.h",
"sys/types.h",
"bits/stdio.h"
# "limits.h"
]
no_includes = true
include_guard = "_STDIO_H"
# trailer = "#include <bits/stdio.h>"
usize_type = "size_t"
isize_type = "ssize_t"
[export]
include = ["fpos_t"]
exclude = [
"printf",
"fprintf",
"dprintf",
"sprintf",
"snprintf",
"scanf",
"fscanf",
"sscanf"
]

View File

@ -0,0 +1,192 @@
use core::{
ffi::{c_char, c_int, c_long, c_void},
ptr::NonNull,
};
use alloc::boxed::Box;
use yggdrasil_rt::{debug_trace, io::OpenOptions, path::Path};
use crate::{
error::{CEofResult, CFdResult, CIntZeroResult, CPtrResult, EResult},
headers::sys_types::off_t,
io::{
self,
managed::{FileOpenSource, FILE},
},
util::{PointerExt, PointerStrExt},
};
use super::fpos_t;
fn open_inner<O: FileOpenSource>(source: O, mode_str: &[u8]) -> EResult<NonNull<FILE>> {
let opts = match mode_str {
b"r" | b"rb" => OpenOptions::READ,
b"r+" | b"rb+" => OpenOptions::WRITE | OpenOptions::READ,
b"w" | b"wb" => OpenOptions::WRITE | OpenOptions::TRUNCATE | OpenOptions::CREATE,
b"w+" | b"wb+" => {
OpenOptions::WRITE | OpenOptions::TRUNCATE | OpenOptions::CREATE | OpenOptions::READ
}
b"a" | b"ab" => {
OpenOptions::APPEND | OpenOptions::CREATE | OpenOptions::READ | OpenOptions::WRITE
}
b"a+" | b"ab+" => {
OpenOptions::APPEND | OpenOptions::CREATE | OpenOptions::READ | OpenOptions::WRITE
}
// TODO
_ => todo!(),
};
let file = source.open_with(opts)?;
let file = NonNull::from(Box::leak(Box::new(file)));
io::managed::register_file(file);
EResult::Ok(file)
}
#[no_mangle]
unsafe extern "C" fn clearerr(fp: *mut FILE) {}
#[no_mangle]
unsafe extern "C" fn fclose(fp: *mut FILE) -> CEofResult {
let fp = NonNull::new(fp).expect("fclose(): fp == NULL");
if !io::managed::deregister_file(fp) {
debug_trace!("fclose() on non-registered file: {fp:p}");
}
FILE::close(fp)?;
CEofResult::success(0)
}
#[no_mangle]
unsafe extern "C" fn fdopen(fd: c_int, mode: *const c_char) -> CPtrResult<FILE> {
todo!()
}
#[no_mangle]
unsafe extern "C" fn feof(fp: *mut FILE) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn ferror(fp: *mut FILE) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn fflush(fp: *mut FILE) -> CIntZeroResult {
todo!()
}
#[no_mangle]
unsafe extern "C" fn fileno(fp: *mut FILE) -> CFdResult {
todo!()
}
#[no_mangle]
unsafe extern "C" fn flockfile(fp: *mut FILE) {
todo!()
}
#[no_mangle]
unsafe extern "C" fn fgetpos(fp: *mut FILE, pos: *mut fpos_t) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn fmemopen(
buffer: *mut c_void,
size: usize,
mode: *const c_char,
) -> CPtrResult<FILE> {
todo!()
}
#[no_mangle]
unsafe extern "C" fn freopen(
path: *const c_char,
mode: *const c_char,
fp: *mut FILE,
) -> CPtrResult<FILE> {
todo!()
}
#[no_mangle]
unsafe extern "C" fn fopen(path: *const c_char, mode: *const c_char) -> CPtrResult<FILE> {
let path = path.ensure_str();
let mode = mode.ensure_cstr();
let file = open_inner(Path::from_str(path), mode.to_bytes())?;
CPtrResult::success(file)
}
#[no_mangle]
unsafe extern "C" fn fseek(fp: *mut FILE, offset: c_long, whence: c_int) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn fseeko(fp: *mut FILE, offset: off_t, whence: c_int) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn fsetpos(fp: *mut FILE, pos: *const fpos_t) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn ftell(fp: *mut FILE) -> c_long {
todo!()
}
#[no_mangle]
unsafe extern "C" fn ftello(fp: *mut FILE) -> off_t {
todo!()
}
#[no_mangle]
unsafe extern "C" fn ftrylockfile(fp: *mut FILE) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn funlockfile(fp: *mut FILE) {
todo!()
}
#[no_mangle]
unsafe extern "C" fn open_memstream(
ptr: *mut *mut c_char,
sizeloc: *mut usize,
) -> CPtrResult<FILE> {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pclose(fp: *mut FILE) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn popen(command: *const c_char, ty: *const c_char) -> CPtrResult<FILE> {
todo!()
}
#[no_mangle]
unsafe extern "C" fn rewind(fp: *mut FILE) {
todo!()
}
#[no_mangle]
unsafe extern "C" fn setbuf(fp: *mut FILE, buf: *mut c_char) {
todo!()
}
#[no_mangle]
unsafe extern "C" fn setvbuf(
fp: *mut FILE,
buffer: *mut c_void,
mode: c_int,
size: usize,
) -> c_int {
todo!()
}

View File

@ -0,0 +1,125 @@
use core::ffi::{c_char, c_int, c_void};
use crate::{
error::CUsizeResult,
io::{managed::FILE, Read, Write},
util::PointerExt,
};
#[no_mangle]
unsafe extern "C" fn fgetc(fp: *mut FILE) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn fgets(buf: *mut c_char, size: c_int, fp: *mut FILE) -> *mut c_char {
todo!()
}
#[no_mangle]
unsafe extern "C" fn fputc(ch: c_int, fp: *mut FILE) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn fputs(str: *const c_char, fp: *mut FILE) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn fread(
buf: *mut c_void,
size: usize,
nmemb: usize,
fp: *mut FILE,
) -> CUsizeResult {
let size = size.checked_mul(nmemb).expect("size * nmemb too large");
let buf = buf.cast::<u8>().ensure_slice_mut(size);
let fp = fp.ensure_mut();
let len = fp.read(buf)?;
CUsizeResult::success(len)
}
#[no_mangle]
unsafe extern "C" fn fwrite(
buf: *const c_void,
size: usize,
nmemb: usize,
fp: *mut FILE,
) -> CUsizeResult {
let size = size.checked_mul(nmemb).expect("size * nmemb too large");
let buf = buf.cast::<u8>().ensure_slice(size);
let fp = fp.ensure_mut();
let len = fp.write(buf)?;
CUsizeResult::success(len)
}
#[no_mangle]
unsafe extern "C" fn getc(fp: *mut FILE) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn getchar() -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn getc_unlocked(fp: *mut FILE) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn getchar_unlocked() -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn getdelim(
lineptr: *mut *mut c_char,
size: *mut usize,
delim: c_int,
fp: *mut FILE,
) -> isize {
todo!()
}
#[no_mangle]
unsafe extern "C" fn getline(lineptr: *mut *mut c_char, size: *mut usize, fp: *mut FILE) -> isize {
todo!()
}
#[no_mangle]
unsafe extern "C" fn gets(buf: *mut c_char) -> *mut c_char {
todo!()
}
#[no_mangle]
unsafe extern "C" fn putc(ch: c_int, fp: *mut FILE) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn putchar(ch: c_int) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn putc_unlocked(ch: c_int, fp: *mut FILE) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn putchar_unlocked(ch: c_int) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn puts(str: *const c_char) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn ungetc(ch: c_int, fp: *mut FILE) -> c_int {
todo!()
}

View File

@ -0,0 +1,26 @@
use core::ffi::c_int;
mod printf;
mod scanf;
mod file;
mod io;
mod util;
pub const _IOFBF: c_int = 0;
pub const _IOLBF: c_int = 1;
pub const _IONBF: c_int = 2;
pub const SEEK_SET: c_int = 0;
pub const SEEK_CUR: c_int = 1;
pub const SEEK_END: c_int = 2;
pub const TMP_MAX: usize = 216000;
pub const EOF: c_int = -1;
pub type fpos_t = u64;
pub const BUFSIZ: usize = 8192;
const UNGETC_MAX: usize = 128;

View File

@ -0,0 +1,166 @@
use core::{ffi::c_double, fmt, num::FpCategory};
use alloc::{format, string::String};
use crate::{error::EResult, headers::errno, io::Write};
use super::format::FmtOpts;
fn abs(f: c_double) -> c_double {
if f.is_sign_negative() {
-f
} else {
f
}
}
fn float_exp(mut val: c_double) -> (c_double, isize) {
let mut exp: isize = 0;
while abs(val) >= 10.0 {
val /= 10.0;
exp += 1;
}
while c_double::EPSILON < abs(val) && abs(val) < 1.0 {
val *= 10.0;
exp -= 1;
}
(val, exp)
}
fn fmt_float_exp<W: Write + fmt::Write>(
val: c_double,
exp: isize,
exp_fmt: u8,
output: &mut W,
precision: usize,
_opts: &FmtOpts,
) -> EResult<usize> {
let mut exp2 = exp;
let mut exp_len = 1;
while exp2 >= 10 {
exp2 /= 10;
exp_len += 1;
}
let string = fmt_float_string(val, precision);
let f_len = output.write(string.as_bytes())?;
let _e_len = match write!(output, "{}{:+03}", exp_fmt as char, exp) {
Ok(count) => count,
// TODO proper error code
Err(_) => return EResult::Err(errno::EINVAL),
};
EResult::Ok(f_len + 2 + 2.max(exp_len))
}
fn fmt_float_string(val: c_double, precision: usize) -> String {
let string = format!("{:.p$}", val, p = precision);
// TODO trim
string
}
fn fmt_float_finite<W: Write + fmt::Write>(
val: c_double,
output: &mut W,
precision: usize,
opts: &FmtOpts,
) -> EResult<usize> {
let s = fmt_float_string(val, precision);
let lpad = opts.left_pad(output, s.len())?;
let flen = output.write(s.as_bytes())?;
let rpad = opts.right_pad(output, s.len())?;
EResult::Ok(lpad + flen + rpad)
}
fn fmt_float_nonfinite<W: Write + fmt::Write>(
val: c_double,
output: &mut W,
upper: bool,
opts: &FmtOpts,
) -> EResult<usize> {
let mut len = 0;
if val.is_sign_negative() {
len += output.write(b"-")?;
}
let nonfinite_str = match val.classify() {
FpCategory::Infinite => match upper {
false => b"inf",
true => b"INF",
},
FpCategory::Nan => match upper {
false => b"nan",
true => b"NaN",
},
_ => unreachable!(),
};
let lpad = opts.left_pad(output, len + nonfinite_str.len())?;
let flen = output.write(nonfinite_str)?;
let rpad = opts.right_pad(output, len + nonfinite_str.len())?;
EResult::Ok(len + lpad + flen + rpad)
}
pub fn fmt_float<W: Write + fmt::Write>(
val: c_double,
output: &mut W,
upper: bool,
opts: &FmtOpts,
) -> EResult<usize> {
if val.is_finite() {
fmt_float_finite(val, output, 6, opts)
} else {
fmt_float_nonfinite(val, output, upper, opts)
}
}
pub fn fmt_float_scientific<W: Write + fmt::Write>(
val: c_double,
output: &mut W,
upper: bool,
opts: &FmtOpts,
) -> EResult<usize> {
if val.is_finite() {
let (val, exp) = float_exp(val);
let exp_fmt = match upper {
false => b'e',
true => b'E',
};
let precision = 6;
fmt_float_exp(val, exp, exp_fmt, output, precision, opts)
} else {
fmt_float_nonfinite(val, output, upper, opts)
}
}
pub fn fmt_float_any<W: Write + fmt::Write>(
val: c_double,
output: &mut W,
upper: bool,
opts: &FmtOpts,
) -> EResult<usize> {
if val.is_finite() {
let (log, exp) = float_exp(val);
let exp_fmt = match upper {
false => b'e',
true => b'E',
};
let precision: usize = 6;
let use_exp_format = exp < -4 || exp >= precision as isize;
if use_exp_format {
let precision = precision.saturating_sub(1);
fmt_float_exp(log, exp, exp_fmt, output, precision, opts)
} else {
let len = 1 + core::cmp::max(0, exp) as usize;
let precision = precision.saturating_sub(len);
fmt_float_finite(val, output, precision, opts)
}
} else {
fmt_float_nonfinite(val, output, upper, opts)
}
}

View File

@ -0,0 +1,307 @@
use core::{
ffi::{
c_char, c_double, c_int, c_long, c_longlong, c_short, c_uchar, c_uint, c_ulong,
c_ulonglong, c_ushort, CStr, VaList,
},
fmt,
};
use alloc::string::String;
use crate::{error::EResult, io::Write, types::wchar_t};
use super::float::{fmt_float, fmt_float_any, fmt_float_scientific};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum FmtSize {
ShortShort,
Short,
Normal,
Long,
LongLong,
Size,
#[allow(unused)]
LongFloat,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum FmtSign {
Default,
Space,
Always,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum FmtSpec {
Integer,
Unsigned(FmtRadix),
AnyFloat(bool),
Float(bool),
ScientificFloat(bool),
String,
Char,
Pointer,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum FmtRadix {
Decimal,
Octal,
Hex(bool),
}
pub struct FmtOpts {
pub size: FmtSize,
pub alternate: bool,
pub pad_char: u8,
pub width: usize,
pub sign: FmtSign,
pub left_adjust: bool,
}
impl Default for FmtOpts {
fn default() -> Self {
Self {
size: FmtSize::Normal,
alternate: false,
pad_char: b' ',
width: 0,
sign: FmtSign::Default,
left_adjust: false,
}
}
}
impl FmtRadix {
const CHARSET_LOWER: &'static [u8] = b"0123456789abcdef";
const CHARSET_UPPER: &'static [u8] = b"0123456789ABCDEF";
pub fn charset_and_divisor(self) -> (&'static [u8], u64) {
match self {
Self::Octal => (Self::CHARSET_LOWER, 8),
Self::Decimal => (Self::CHARSET_LOWER, 10),
Self::Hex(true) => (Self::CHARSET_UPPER, 16),
Self::Hex(false) => (Self::CHARSET_LOWER, 16),
}
}
}
impl FmtSize {
fn arg_int(&self, ap: &mut VaList) -> i64 {
match self {
Self::ShortShort => unsafe { ap.arg::<c_char>() as _ },
Self::Short => unsafe { ap.arg::<c_short>() as _ },
Self::Normal => unsafe { ap.arg::<c_int>() as _ },
Self::Long => unsafe { ap.arg::<c_long>() as _ },
Self::LongLong => unsafe { ap.arg::<c_longlong>() as _ },
Self::Size => unsafe { ap.arg::<isize>() as _ },
Self::LongFloat => panic!("Incorrect L used with ints"),
}
}
fn arg_uint(&self, ap: &mut VaList) -> u64 {
match self {
Self::ShortShort => unsafe { ap.arg::<c_uchar>() as _ },
Self::Short => unsafe { ap.arg::<c_ushort>() as _ },
Self::Normal => unsafe { ap.arg::<c_uint>() as _ },
Self::Long => unsafe { ap.arg::<c_ulong>() as _ },
Self::LongLong => unsafe { ap.arg::<c_ulonglong>() as _ },
Self::Size => unsafe { ap.arg::<usize>() as _ },
Self::LongFloat => panic!("Incorrect L used with uints"),
}
}
fn arg_float(&self, ap: &mut VaList) -> c_double {
match self {
Self::Normal | Self::Long => unsafe { ap.arg::<c_double>() },
Self::LongFloat => unimplemented!("Long floats are not implemented yet"),
_ => panic!("Incorrect size specifier used with floats"),
}
}
fn arg_pointer(&self, ap: &mut VaList) -> usize {
if *self != Self::Normal {
panic!("Incorrect size specifier used with a pointer");
}
unsafe { ap.arg::<usize>() }
}
pub fn shorter(self) -> Self {
match self {
Self::Normal => Self::Short,
Self::Short => Self::ShortShort,
_ => self,
}
}
pub fn longer(self) -> Self {
match self {
Self::Normal => Self::Long,
Self::Long => Self::LongLong,
_ => self,
}
}
}
impl FmtOpts {
fn pad<W: Write + fmt::Write>(&self, output: &mut W, len: usize) -> EResult<usize> {
let pad = self.width - core::cmp::min(self.width, len);
for _ in 0..pad {
output.write(&[self.pad_char])?;
}
EResult::Ok(pad)
}
pub fn left_pad<W: Write + fmt::Write>(&self, output: &mut W, len: usize) -> EResult<usize> {
if !self.left_adjust {
self.pad(output, len)
} else {
EResult::Ok(0)
}
}
pub fn right_pad<W: Write + fmt::Write>(&self, output: &mut W, len: usize) -> EResult<usize> {
if self.left_adjust {
self.pad(output, len)
} else {
EResult::Ok(0)
}
}
pub fn fmt<W: Write + fmt::Write>(
&self,
spec: FmtSpec,
output: &mut W,
ap: &mut VaList,
) -> EResult<usize> {
let mut buffer = [0; 64];
let len = match spec {
FmtSpec::Integer => {
let val = self.size.arg_int(ap);
fmt_signed_int(val, &mut buffer)
}
FmtSpec::Unsigned(radix) => {
let val = self.size.arg_uint(ap);
fmt_unsigned_int(val, &mut buffer, radix)
}
FmtSpec::Pointer => {
let val = self.size.arg_pointer(ap);
if val == 0 {
buffer[..5].copy_from_slice(b"(nil)");
5
} else {
fmt_unsigned_int(val as u64, &mut buffer, FmtRadix::Hex(false))
}
}
// TODO string precision
FmtSpec::String if self.size == FmtSize::Normal => {
let val = unsafe { ap.arg::<*const c_char>() };
if val.is_null() {
buffer[..5].copy_from_slice(b"(nil)");
5
} else {
let val = unsafe { CStr::from_ptr(val) };
let bytes = val.to_bytes();
let lpad = self.left_pad(output, bytes.len())?;
let len = output.write(bytes)?;
let rpad = self.right_pad(output, bytes.len())?;
return EResult::Ok(lpad + len + rpad);
}
}
FmtSpec::String if self.size == FmtSize::Long => {
let mut val = unsafe { ap.arg::<*const wchar_t>() };
if val.is_null() {
buffer[..5].copy_from_slice(b"(nil)");
5
} else {
let mut string = String::new();
unsafe {
while *val != 0 {
let c = match char::from_u32(*val as _) {
Some(c) => c,
None => {
// TODO EILSEQ
todo!();
}
};
string.push(c);
val = val.add(1);
}
}
let bytes = string.as_bytes();
let lpad = self.left_pad(output, bytes.len())?;
let len = output.write(bytes)?;
let rpad = self.right_pad(output, bytes.len())?;
return EResult::Ok(lpad + len + rpad);
}
}
FmtSpec::Char if self.size == FmtSize::Normal => {
let ch = unsafe { ap.arg::<c_char>() } as u8;
buffer[0] = ch;
1
}
FmtSpec::Char if self.size == FmtSize::Long => {
todo!();
}
FmtSpec::Float(upper) => {
let val = self.size.arg_float(ap);
return fmt_float(val, output, upper, self);
}
FmtSpec::AnyFloat(upper) => {
let val = self.size.arg_float(ap);
return fmt_float_any(val, output, upper, self);
}
FmtSpec::ScientificFloat(upper) => {
let val = self.size.arg_float(ap);
return fmt_float_scientific(val, output, upper, self);
}
// Incorrect cases
FmtSpec::Char => {
panic!("Incorrect size specifier used with a C char");
}
FmtSpec::String => {
panic!("Incorrect size specifier used with a C string");
}
};
let lpad = self.left_pad(output, len)?;
let count = output.write(&buffer[..len])?;
let rpad = self.right_pad(output, len)?;
EResult::Ok(lpad + count + rpad)
}
}
fn fmt_unsigned_int(mut value: u64, output: &mut [u8], radix: FmtRadix) -> usize {
if value == 0 {
output[0] = b'0';
return 1;
}
let (charset, divisor) = radix.charset_and_divisor();
let mut pos = 0;
while value != 0 {
output[pos] = charset[(value % divisor) as usize];
value /= divisor;
pos += 1;
}
output[..pos].reverse();
pos
}
fn fmt_signed_int(value: i64, output: &mut [u8]) -> usize {
if value < 0 {
output[0] = b'-';
1 + fmt_unsigned_int(
value.wrapping_neg() as u64,
&mut output[1..],
FmtRadix::Decimal,
)
} else {
fmt_unsigned_int(value as u64, output, FmtRadix::Decimal)
}
}

View File

@ -0,0 +1,266 @@
use core::{
ffi::{c_char, c_int, VaList},
fmt,
ptr::NonNull,
};
use format::{FmtOpts, FmtRadix, FmtSign, FmtSize, FmtSpec};
use crate::{
error::{CIntCountResult, EResult, TryFromExt},
io::{
managed::{stdout, FILE},
raw::RawFile,
Write,
},
util::{PointerExt, PointerStrExt},
};
mod float;
mod format;
struct FileWriter<'w, W: Write>(&'w mut W);
struct StringWriter {
buffer: NonNull<c_char>,
position: usize,
capacity: usize,
}
trait FmtWriter: Write + fmt::Write {}
impl<'w, W: Write> fmt::Write for FileWriter<'w, W> {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.0
.write_all(s.as_bytes())
.into_result(|_| fmt::Error, true)
}
}
impl<'w, W: Write> Write for FileWriter<'w, W> {
fn write(&mut self, data: &[u8]) -> EResult<usize> {
self.0.write(data)
}
fn flush(&mut self) -> EResult<()> {
self.0.flush()
}
}
impl<'w, W: Write> FmtWriter for FileWriter<'w, W> {}
impl StringWriter {
pub fn new(buffer: NonNull<c_char>, capacity: usize) -> Self {
Self {
buffer,
capacity,
position: 0,
}
}
}
impl fmt::Write for StringWriter {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.write(s.as_bytes()).into_result(|_| fmt::Error, true)?;
Ok(())
}
}
impl Write for StringWriter {
fn write(&mut self, data: &[u8]) -> EResult<usize> {
let count = core::cmp::min(self.capacity - self.position, data.len());
if count > 0 {
let dst = unsafe {
core::slice::from_raw_parts_mut(
self.buffer.add(self.position).as_ptr().cast(),
count,
)
};
dst.copy_from_slice(&data[..count]);
self.position += count;
}
EResult::Ok(count)
}
fn flush(&mut self) -> EResult<()> {
EResult::Ok(())
}
}
impl FmtWriter for StringWriter {}
fn printf_inner<W: FmtWriter>(output: &mut W, format: &[u8], mut ap: VaList) -> EResult<usize> {
let mut fmt = format.into_iter();
let mut count = 0;
while let Some(&c) = fmt.next() {
if c != b'%' {
count += output.write(&[c])?;
continue;
}
let mut opts = FmtOpts::default();
let mut cur = fmt.next();
// Parse flag characters
while let Some(&c) = cur {
match c {
b'#' => opts.alternate = true,
b'0' => opts.pad_char = b'0',
b'-' => opts.left_adjust = true,
b' ' => opts.sign = FmtSign::Space,
b'+' => opts.sign = FmtSign::Always,
b'\'' => unimplemented!("The ' flag is not implemented"),
_ => break,
}
cur = fmt.next();
}
// TODO Field width
while let Some(&c) = cur {
if c.is_ascii_digit() {
opts.width *= 10;
opts.width += (c - b'0') as usize;
cur = fmt.next();
} else {
break;
}
}
// TODO Precision
// Length modifier
while let Some(&c) = cur {
match c {
b'h' => opts.size = opts.size.shorter(),
b'l' => opts.size = opts.size.longer(),
b'q' => opts.size = FmtSize::LongLong,
b'L' => todo!(),
b'j' => todo!(),
b'z' | b'Z' => opts.size = FmtSize::Size,
b't' => todo!(),
_ => break,
}
cur = fmt.next();
}
// Conversion specifier
let mut spec = None;
if let Some(&c) = cur {
match c {
b'd' | b'i' => spec = Some(FmtSpec::Integer),
b'o' => spec = Some(FmtSpec::Unsigned(FmtRadix::Octal)),
b'u' => spec = Some(FmtSpec::Unsigned(FmtRadix::Decimal)),
b'x' | b'X' => {
spec = Some(FmtSpec::Unsigned(FmtRadix::Hex(c.is_ascii_uppercase())))
}
b'e' | b'E' => spec = Some(FmtSpec::ScientificFloat(c.is_ascii_uppercase())),
b'f' | b'F' => spec = Some(FmtSpec::Float(c.is_ascii_uppercase())),
b'g' | b'G' => spec = Some(FmtSpec::AnyFloat(c.is_ascii_uppercase())),
b'a' | b'A' => unimplemented!("%a/%A are not implemented"),
b'c' => spec = Some(FmtSpec::Char),
b's' => spec = Some(FmtSpec::String),
b'C' => {
opts.size = FmtSize::Long;
spec = Some(FmtSpec::Char);
}
b'S' => {
opts.size = FmtSize::Long;
spec = Some(FmtSpec::String);
}
b'p' => spec = Some(FmtSpec::Pointer),
b'n' => todo!(),
b'm' => todo!(),
b'%' => {
count += output.write(b"%")?;
continue;
}
_ => (),
}
}
if let Some(spec) = spec {
count += opts.fmt(spec, output, &mut ap)?;
}
}
EResult::Ok(count)
}
#[no_mangle]
unsafe extern "C" fn dprintf(fd: c_int, fmt: *const c_char, mut args: ...) -> CIntCountResult {
vdprintf(fd, fmt, args.as_va_list())
}
#[no_mangle]
unsafe extern "C" fn vdprintf(fd: c_int, fmt: *const c_char, args: VaList) -> CIntCountResult {
let mut file = RawFile::e_try_from(fd)?;
let fmt = fmt.ensure_cstr();
let mut writer = FileWriter(&mut file);
let count = printf_inner(&mut writer, fmt.to_bytes(), args)?;
CIntCountResult::success(count.try_into().unwrap())
}
#[no_mangle]
unsafe extern "C" fn printf(fmt: *const c_char, mut args: ...) -> CIntCountResult {
vfprintf(stdout, fmt, args.as_va_list())
}
#[no_mangle]
unsafe extern "C" fn vprintf(fmt: *const c_char, args: VaList) -> CIntCountResult {
vfprintf(stdout, fmt, args)
}
#[no_mangle]
unsafe extern "C" fn fprintf(fp: *mut FILE, fmt: *const c_char, mut args: ...) -> CIntCountResult {
vfprintf(fp, fmt, args.as_va_list())
}
#[no_mangle]
unsafe extern "C" fn vfprintf(fp: *mut FILE, fmt: *const c_char, args: VaList) -> CIntCountResult {
let fp = fp.ensure_mut();
let fmt = fmt.ensure_cstr();
let mut writer = FileWriter(fp);
let count = printf_inner(&mut writer, fmt.to_bytes(), args)?;
CIntCountResult::success(count.try_into().unwrap())
}
#[no_mangle]
unsafe extern "C" fn sprintf(
buffer: *mut c_char,
fmt: *const c_char,
mut args: ...
) -> CIntCountResult {
vsnprintf(buffer, usize::MAX, fmt, args.as_va_list())
}
#[no_mangle]
unsafe extern "C" fn snprintf(
buffer: *mut c_char,
capacity: usize,
fmt: *const c_char,
mut args: ...
) -> CIntCountResult {
vsnprintf(buffer, capacity, fmt, args.as_va_list())
}
#[no_mangle]
unsafe extern "C" fn vsprintf(
buffer: *mut c_char,
fmt: *const c_char,
args: VaList,
) -> CIntCountResult {
vsnprintf(buffer, usize::MAX, fmt, args)
}
#[no_mangle]
unsafe extern "C" fn vsnprintf(
buffer: *mut c_char,
capacity: usize,
fmt: *const c_char,
args: VaList,
) -> CIntCountResult {
let buffer = NonNull::new(buffer).expect("vsnprintf(): buffer == NULL");
let mut writer = StringWriter::new(buffer, capacity);
let fmt = fmt.ensure_cstr();
let count = printf_inner(&mut writer, fmt.to_bytes(), args)?;
CIntCountResult::success(count.try_into().unwrap())
}

View File

@ -0,0 +1,115 @@
use core::ops::RangeInclusive;
use super::FormatError;
pub struct ScanCharSet {
mask: [u64; 4],
invert: bool,
}
impl ScanCharSet {
pub fn new() -> Self {
Self {
mask: [0; 4],
invert: false,
}
}
pub fn from_format_iter<'a, I: Iterator<Item = &'a u8>>(
mut it: I,
) -> Result<(I, Self), FormatError> {
let mut maybe_close_bracket = true;
let mut hyphen = false;
let mut start: Option<u8> = None;
let mut set = ScanCharSet::new();
while let Some(&ch) = it.next() {
if ch == b'^' && maybe_close_bracket {
// "[^...]"
set.set_invert();
continue;
}
if ch == b']' {
if !maybe_close_bracket {
if hyphen {
// "[...-]"
set.insert(b'-');
}
break;
} else {
debug_assert!(!hyphen);
// "[]...]" or "[^]...]"
maybe_close_bracket = false;
set.insert(b']');
continue;
}
}
maybe_close_bracket = false;
if ch == b'-' {
if hyphen {
// "--"?
return Err(FormatError);
}
hyphen = true;
continue;
}
// Any other character
if hyphen {
let Some(start) = start.take() else {
// "-x" without start?
return Err(FormatError);
};
let same_category = (start.is_ascii_digit() && ch.is_ascii_digit())
|| (start.is_ascii_uppercase() && ch.is_ascii_uppercase())
|| (start.is_ascii_lowercase() && ch.is_ascii_lowercase());
if same_category && ch > start {
hyphen = false;
set.insert_range(start..=ch);
} else {
return Err(FormatError);
}
} else {
if let Some(start) = start.replace(ch) {
// "c" without range
set.insert(ch);
}
}
}
Ok((it, set))
}
pub fn set_invert(&mut self) {
self.invert = true;
}
pub fn insert(&mut self, ch: u8) {
let index = (ch / 64) as usize;
let bit = 1 << (ch % 64);
self.mask[index] |= bit;
}
pub fn insert_range(&mut self, range: RangeInclusive<u8>) {
for ch in range {
self.insert(ch);
}
}
pub fn contains(&self, ch: u8) -> bool {
let index = (ch / 64) as usize;
let bit = 1 << (ch % 64);
self.mask[index] & bit == bit
}
pub fn check(&self, ch: u8) -> bool {
self.invert ^ self.contains(ch)
}
}

View File

@ -0,0 +1,434 @@
use core::ffi::{
c_char, c_int, c_long, c_longlong, c_short, c_uchar, c_uint, c_ulong, c_ulonglong, c_ushort,
c_void, VaList,
};
use super::{
char_set::ScanCharSet,
reader::{GetChar, ScanReader},
ConversionError,
};
pub enum ScanRadix {
Decimal,
Oct,
Hex,
Auto,
}
pub enum ScanSpec {
// %s, a sequence of non-whitespace characters
Word,
// %c, a sequence of characters whose length is specified by field width (default 1)
Chars(ScanCharSet),
// %[...], nonempty sequence of characters from the specified set
AnyChar,
// %d, %u, %x/%X, %o, %i, deprecated, optionally-signed/unsigned decimals
Signed(ScanRadix),
Unsigned(ScanRadix),
// %n, reports consumed count
ConsumedCount,
// %p
Pointer,
// %%
Percent,
}
pub enum ScanSize {
ShortShort,
Short,
Normal,
Long,
LongLong,
Size,
}
pub struct ScanOpt {
pub width: Option<usize>,
pub size: ScanSize,
pub discard: bool,
}
impl ScanSize {
pub fn shorter(self) -> ScanSize {
match self {
Self::Normal => Self::Short,
Self::Short => Self::ShortShort,
_ => self,
}
}
pub fn longer(self) -> ScanSize {
match self {
Self::Normal => Self::Long,
Self::Long => Self::LongLong,
_ => self,
}
}
pub unsafe fn write_int(&self, value: i64, ap: &mut VaList) {
let raw = ap.arg::<*mut c_void>();
if raw.is_null() {
panic!();
}
match self {
Self::ShortShort => *(raw as *mut c_char) = value as _,
Self::Short => *(raw as *mut c_short) = value as _,
Self::Normal => *(raw as *mut c_int) = value as _,
Self::Long => *(raw as *mut c_long) = value as _,
Self::LongLong => *(raw as *mut c_longlong) = value as _,
Self::Size => *(raw as *mut isize) = value as _,
}
}
pub unsafe fn write_uint(&self, value: u64, ap: &mut VaList) {
let raw = ap.arg::<*mut c_void>();
if raw.is_null() {
panic!();
}
match self {
Self::ShortShort => *(raw as *mut c_uchar) = value as _,
Self::Short => *(raw as *mut c_ushort) = value as _,
Self::Normal => *(raw as *mut c_uint) = value as _,
Self::Long => *(raw as *mut c_ulong) = value as _,
Self::LongLong => *(raw as *mut c_ulonglong) = value as _,
Self::Size => *(raw as *mut usize) = value as _,
}
}
}
impl Default for ScanOpt {
fn default() -> Self {
Self {
width: None,
size: ScanSize::Normal,
discard: false,
}
}
}
impl ScanOpt {
pub fn scan<G: GetChar>(
self,
stream: &mut ScanReader<G>,
spec: ScanSpec,
ap: &mut VaList,
) -> Result<usize, ConversionError> {
let n = if self.discard { 0 } else { 1 };
match (spec, self.size) {
(ScanSpec::Percent, _) => todo!(),
(ScanSpec::Word, ScanSize::Normal) => {
let width = self.width.unwrap_or(usize::MAX);
let dst = if self.discard {
None
} else {
let dst = unsafe { ap.arg::<*mut c_char>() };
if dst.is_null() {
panic!();
}
Some(dst)
};
scan_word(stream, dst, width)?;
Ok(n)
}
(ScanSpec::Chars(set), ScanSize::Normal) => {
let width = self.width.unwrap_or(usize::MAX);
let dst = if self.discard {
None
} else {
let dst = unsafe { ap.arg::<*mut c_char>() };
if dst.is_null() {
panic!();
}
Some(dst)
};
scan_char_set(stream, dst, set, width)?;
Ok(n)
}
(ScanSpec::AnyChar, ScanSize::Normal) => {
let width = self.width.unwrap_or(1);
let dst = if self.discard {
None
} else {
let dst = unsafe { ap.arg::<*mut c_char>() };
if dst.is_null() {
panic!();
}
Some(dst)
};
scan_chars(stream, dst, width)?;
Ok(n)
}
(ScanSpec::Word, _) => todo!("%ls"),
(ScanSpec::Chars(_), _) => todo!("%l[...]"),
(ScanSpec::AnyChar, _) => todo!("%lc"),
(ScanSpec::Signed(radix), size) => {
let value = scan_signed(stream, radix)?;
if !self.discard {
unsafe {
size.write_int(value, ap);
}
}
Ok(n)
}
(ScanSpec::Unsigned(radix), size) => {
let value = scan_unsigned(stream, radix)?;
if !self.discard {
unsafe {
size.write_uint(value, ap);
}
}
Ok(n)
}
(ScanSpec::Pointer, _) => todo!(),
(ScanSpec::ConsumedCount, size) => {
if !self.discard {
unsafe {
size.write_int(stream.consumed_count().try_into().unwrap(), ap);
}
}
Ok(0)
}
}
}
}
fn scan_word<G: GetChar>(
stream: &mut ScanReader<G>,
mut dst: Option<*mut c_char>,
limit: usize,
) -> Result<(), ConversionError> {
// Read until limit reached or whitespace/EOF
let mut matched = 0;
while matched < limit {
let Some(ch) = stream.peek()? else {
break;
};
if ch.is_ascii_whitespace() {
break;
}
if let Some(dst) = dst.as_mut() {
unsafe {
**dst = ch as _;
*dst = dst.add(1);
}
}
matched += 1;
stream.next()?;
}
if matched == 0 {
return Err(ConversionError::ConversionFailed);
}
if let Some(dst) = dst.as_mut() {
unsafe {
**dst = 0;
}
}
Ok(())
}
fn scan_chars<G: GetChar>(
stream: &mut ScanReader<G>,
mut dst: Option<*mut c_char>,
width: usize,
) -> Result<(), ConversionError> {
let mut matched = 0;
while matched < width {
let Some(ch) = stream.next()? else {
break;
};
if let Some(dst) = dst.as_mut() {
unsafe {
**dst = ch as _;
*dst = dst.add(1);
}
}
matched += 1;
stream.next()?;
}
if matched == width {
Ok(())
} else {
Err(ConversionError::ConversionFailed)
}
}
fn scan_char_set<G: GetChar>(
stream: &mut ScanReader<G>,
mut dst: Option<*mut c_char>,
set: ScanCharSet,
width: usize,
) -> Result<(), ConversionError> {
let mut matched = 0;
while matched < width {
let Some(ch) = stream.peek()? else {
break;
};
if !set.check(ch) {
break;
}
if let Some(dst) = dst.as_mut() {
unsafe {
**dst = ch as _;
*dst = dst.add(1);
}
}
stream.next()?;
matched += 1;
}
if matched == 0 {
return Err(ConversionError::ConversionFailed);
}
if let Some(dst) = dst.as_mut() {
unsafe {
**dst = 0;
}
}
Ok(())
}
fn scan_unsigned<G: GetChar>(
stream: &mut ScanReader<G>,
radix: ScanRadix,
) -> Result<u64, ConversionError> {
let mut value = 0;
let mut matched = 0;
match radix {
ScanRadix::Auto => {
let Some(maybe_0) = stream.peek()? else {
return Err(ConversionError::ConversionFailed);
};
if maybe_0 == b'0' {
stream.next()?;
// Maybe x/X or octal
let Some(maybe_next) = stream.peek()? else {
// At least matched a zero
return Ok(0);
};
return match maybe_next {
b'x' | b'X' => {
stream.next()?;
scan_unsigned(stream, ScanRadix::Hex)
}
b'0'..=b'9' => scan_unsigned(stream, ScanRadix::Oct),
_ => Err(ConversionError::ConversionFailed),
};
} else {
// Anything else is a decimal
return scan_unsigned(stream, ScanRadix::Decimal);
}
}
ScanRadix::Oct => {
while let Some(ch) = stream.peek()? {
let digit = match ch {
b'0'..=b'7' => (ch - b'0') as u64,
_ => break,
};
stream.next()?;
value <<= 3;
value |= digit;
matched += 1;
}
}
ScanRadix::Decimal => {
while let Some(ch) = stream.peek()? {
let digit = match ch {
b'0'..=b'9' => (ch - b'0') as u64,
_ => break,
};
stream.next()?;
value *= 10;
value += digit;
matched += 1;
}
}
ScanRadix::Hex => {
while let Some(ch) = stream.peek()? {
let digit = match ch {
b'0'..=b'9' => (ch - b'0') as u64,
b'a'..=b'f' => (ch - b'a') as u64 + 10,
b'A'..=b'F' => (ch - b'A') as u64 + 10,
_ => break,
};
stream.next()?;
value <<= 4;
value |= digit;
matched += 1;
}
}
}
if matched != 0 {
Ok(value)
} else {
// No characters matched
Err(ConversionError::ConversionFailed)
}
}
fn scan_signed<G: GetChar>(
stream: &mut ScanReader<G>,
radix: ScanRadix,
) -> Result<i64, ConversionError> {
let sign = if let Some(ch) = stream.peek()? {
if ch == b'-' {
stream.next()?;
false
} else {
true
}
} else {
// EOF
return Err(ConversionError::ConversionFailed);
};
let value: i64 = scan_unsigned(stream, radix)?
.try_into()
.map_err(|_| ConversionError::ConversionFailed)?;
Ok(if sign { value } else { -value })
}

View File

@ -0,0 +1,207 @@
use core::ffi::{c_char, VaList};
use char_set::ScanCharSet;
use format::{ScanOpt, ScanRadix, ScanSize, ScanSpec};
use reader::{GetChar, ScanReader};
use crate::{
error::{CEofResult, EResult},
headers::errno::{self, Errno},
io::managed::{stdin, FILE}, util::{PointerExt, PointerStrExt},
};
mod char_set;
mod format;
mod reader;
pub enum ConversionError {
Error(Errno),
ConversionFailed,
}
pub struct FormatError;
impl From<Errno> for ConversionError {
fn from(value: Errno) -> Self {
Self::Error(value)
}
}
fn scanf_inner<G: GetChar>(
stream: &mut ScanReader<G>,
format: &[u8],
mut ap: VaList,
) -> EResult<usize> {
let mut it = format.into_iter();
let mut matched = 0;
let mut skip_space = false;
while let Some(&ch) = it.next() {
if ch == b' ' {
// Any amount of whitespace
if skip_space {
continue;
}
// Skip following whitespace in format
skip_space = true;
// Skip whitespace in stream
while let Some(ic) = stream.peek()? {
if !ic.is_ascii_whitespace() {
break;
}
stream.next()?;
}
continue;
} else if ch != b'%' {
// Regular character
skip_space = false;
let ic = stream.peek()?;
if !ic.map(|ic| ic == ch).unwrap_or(false) {
break;
}
stream.next()?;
continue;
}
skip_space = false;
// %*123lld
let mut opts = ScanOpt::default();
let mut cur = it.next();
if let Some(&ch) = cur {
if ch == b'*' {
opts.discard = true;
cur = it.next();
}
}
while let Some(&ch) = cur {
if ch.is_ascii_digit() {
opts.width
.replace(opts.width.unwrap_or(0) * 10 + (ch - b'0') as usize);
cur = it.next();
} else {
break;
}
}
while let Some(&ch) = cur {
match ch {
b'h' => opts.size = opts.size.shorter(),
b'l' => opts.size = opts.size.longer(),
b'z' => opts.size = ScanSize::Size,
// TODO ptrdiff_t
b't' => todo!(),
// TODO intmax_t/uintmax_t
b'j' => todo!(),
// TODO floats
b'L' | b'q' => todo!(),
_ => break,
}
cur = it.next();
}
let spec = if let Some(&ch) = cur {
match ch {
b'i' => ScanSpec::Signed(ScanRadix::Auto),
b'd' => ScanSpec::Signed(ScanRadix::Decimal),
b'x' | b'X' => ScanSpec::Unsigned(ScanRadix::Hex),
b'u' => ScanSpec::Unsigned(ScanRadix::Decimal),
b'o' => ScanSpec::Unsigned(ScanRadix::Oct),
b's' => ScanSpec::Word,
b'[' => {
let (new_it, set) = match ScanCharSet::from_format_iter(it) {
Ok(v) => v,
Err(FormatError) => return EResult::Err(errno::ESUCCESS),
};
it = new_it;
ScanSpec::Chars(set)
}
b'c' => ScanSpec::AnyChar,
b'n' => ScanSpec::ConsumedCount,
b'p' => ScanSpec::Pointer,
b'%' => ScanSpec::Percent,
b'f' | b'e' | b'g' | b'E' | b'a' => todo!(),
// Unrecognized specifier
_ => return EResult::Err(errno::ESUCCESS),
}
} else {
// No specifier after %
return EResult::Err(errno::ESUCCESS);
};
matched += match opts.scan(stream, spec, &mut ap) {
Ok(matched) => matched,
Err(ConversionError::ConversionFailed) => break,
Err(ConversionError::Error(err)) => return EResult::Err(err),
};
}
// TODO report end of stream before anything is matched
EResult::Ok(matched)
}
#[no_mangle]
unsafe extern "C" fn scanf(format: *const c_char, mut args: ...) -> CEofResult {
vfscanf(stdin, format, args.as_va_list())
}
#[no_mangle]
unsafe extern "C" fn fscanf(
fp: *mut FILE,
format: *const c_char,
mut args: ...
) -> CEofResult {
vfscanf(fp, format, args.as_va_list())
}
#[no_mangle]
unsafe extern "C" fn vscanf(format: *const c_char, args: VaList) -> CEofResult {
vfscanf(stdin, format, args)
}
#[no_mangle]
unsafe extern "C" fn vfscanf(
fp: *mut FILE,
format: *const c_char,
args: VaList,
) -> CEofResult {
let fp = fp.ensure_mut();
let format = format.ensure_cstr();
let mut reader = ScanReader::new(fp);
let count = scanf_inner(&mut reader, format.to_bytes(), args)?;
CEofResult::success(count.try_into().unwrap())
}
#[no_mangle]
unsafe extern "C" fn sscanf(
input: *const c_char,
format: *const c_char,
mut args: ...
) -> CEofResult {
vsscanf(input, format, args.as_va_list())
}
#[no_mangle]
unsafe extern "C" fn vsscanf(
input: *const c_char,
format: *const c_char,
args: VaList,
) -> CEofResult {
let input = input.ensure_cstr();
let format = format.ensure_cstr();
let mut str_it = input.to_bytes().into_iter();
let mut reader = ScanReader::new(&mut str_it);
let count = scanf_inner(&mut reader, format.to_bytes(), args)?;
CEofResult::success(count.try_into().unwrap())
}

View File

@ -0,0 +1,66 @@
use crate::{error::EResult, io::{managed::FILE, Read}};
pub trait GetChar {
fn getc(&mut self) -> EResult<Option<u8>>;
}
pub struct ScanReader<'a, G: GetChar> {
getter: &'a mut G,
buffer: Option<u8>,
consumed_count: usize,
}
impl GetChar for FILE {
fn getc(&mut self) -> EResult<Option<u8>> {
let mut buf = [0];
match self.read(&mut buf) {
EResult::Ok(1) => EResult::Ok(Some(buf[0])),
EResult::Ok(_) => EResult::Ok(None),
EResult::Err(err) => EResult::Err(err),
}
}
}
impl<'a, I: Iterator<Item = &'a u8>> GetChar for I {
fn getc(&mut self) -> EResult<Option<u8>> {
EResult::Ok(self.next().copied())
}
}
impl<'a, G: GetChar> ScanReader<'a, G> {
pub fn new(getter: &'a mut G) -> Self {
Self {
getter,
buffer: None,
consumed_count: 0,
}
}
fn getc(&mut self) -> EResult<Option<u8>> {
match self.getter.getc() {
EResult::Ok(Some(n)) => {
self.consumed_count += 1;
EResult::Ok(Some(n))
}
e => e,
}
}
pub fn consumed_count(&self) -> usize {
self.consumed_count
}
pub fn peek(&mut self) -> EResult<Option<u8>> {
if self.buffer.is_none() {
self.buffer = self.getc()?;
}
EResult::Ok(self.buffer)
}
pub fn next(&mut self) -> EResult<Option<u8>> {
match self.buffer.take() {
Some(v) => EResult::Ok(Some(v)),
None => self.getc(),
}
}
}

View File

@ -0,0 +1,44 @@
use core::ffi::{c_char, c_int};
use crate::{error::CPtrResult, io::managed::FILE};
#[no_mangle]
unsafe extern "C" fn perror(message: *const c_char) {
todo!()
}
#[no_mangle]
unsafe extern "C" fn ctermid(buf: *mut c_char) -> *mut c_char {
todo!()
}
#[no_mangle]
unsafe extern "C" fn remove(path: *const c_char) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn rename(src: *const c_char, dst: *const c_char) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn renameat(atfd: c_int, src: *const c_char, dst: *const c_char) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn tempnam(dir: *const c_char, prefix: *const c_char) -> *mut c_char {
todo!()
}
#[no_mangle]
unsafe extern "C" fn tmpfile() -> CPtrResult<FILE> {
todo!()
}
#[no_mangle]
unsafe extern "C" fn tmpnam(template: *mut c_char) -> *mut c_char {
todo!()
}

View File

@ -0,0 +1,12 @@
language = "C"
style = "Type"
sys_includes = [
"stddef.h"
]
no_includes = true
include_guard = "_STDLIB_H"
usize_type = "size_t"
isize_type = "ssize_t"

View File

@ -0,0 +1,8 @@
use core::ffi::c_void;
use crate::error::CPtrResult;
#[no_mangle]
pub unsafe extern "C" fn malloc(size: usize) -> CPtrResult<c_void> {
todo!()
}

View File

@ -0,0 +1,30 @@
language = "C"
style = "Type"
sys_includes = ["stdint.h", "stddef.h"]
no_includes = true
include_guard = "_SYS_TYPES_H"
[export]
include = [
"blkcnt_t",
"blksize_t",
"clock_t",
"clockid_t",
"dev_t",
"fsblkcnt_t",
"fsfilcnt_t",
"gid_t",
"id_t",
"ino_t",
"mode_t",
"nlink_t",
"off_t",
"pid_t",
"ssize_t",
"suseconds_t",
"time_t",
"timer_t",
"uid_t"
]

View File

@ -0,0 +1,34 @@
use core::ffi::{c_int, c_ulong};
pub type pid_t = i32;
pub type uid_t = i32;
pub type gid_t = i32;
pub type id_t = i32;
pub type dev_t = u32;
pub type clockid_t = i32;
pub type timer_t = i32;
pub type mode_t = c_int;
#[cfg(any(target_pointer_width = "64", rust_analyzer))]
pub type ssize_t = i64;
pub type blkcnt_t = i64;
pub type blksize_t = c_ulong;
pub type nlink_t = u64;
pub type fsblkcnt_t = u64;
pub type fsfilcnt_t = u64;
pub type ino_t = u64;
pub type clock_t = u64;
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
#[repr(transparent)]
pub struct time_t(pub i64);
pub type off_t = i64;
pub type suseconds_t = c_ulong;

View File

@ -0,0 +1,21 @@
language = "C"
style = "Type"
sys_includes = [
"stddef.h",
"sys/types.h"
]
no_includes = true
include_guard = "_UNISTD_H"
trailer = "#include <bits/unistd.h>"
usize_type = "size_t"
isize_type = "ssize_t"
[export]
exclude = [
"execl",
"execlp",
"execle"
]

View File

@ -0,0 +1,46 @@
use core::ffi::{c_char, c_int, va_list, VaList};
use crate::error::CIntZeroResult;
#[no_mangle]
unsafe extern "C" fn execl(path: *const c_char, arg: *const c_char, args: ...) -> CIntZeroResult {
todo!()
}
#[no_mangle]
unsafe extern "C" fn execlp(file: *const c_char, arg: *const c_char, args: ...) -> CIntZeroResult {
todo!()
}
#[no_mangle]
unsafe extern "C" fn execle(path: *const c_char, arg: *const c_char, args: ...) -> CIntZeroResult {
todo!()
}
#[no_mangle]
unsafe extern "C" fn execv(path: *const c_char, argv: *const *mut c_char) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn execp(file: *const c_char, argv: *const *mut c_char) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn execvpe(
file: *const c_char,
argv: *const *mut c_char,
envp: *const *mut c_char,
) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn fexecve(
fd: c_int,
argv: *const *mut c_char,
envp: *const *mut c_char,
) -> c_int {
todo!()
}

View File

@ -0,0 +1,122 @@
use core::ffi::{c_char, c_int, c_long};
use crate::headers::sys_types::{gid_t, off_t, uid_t};
#[no_mangle]
unsafe extern "C" fn access(path: *const c_char, mode: c_int) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn chdir(path: *const c_char) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn chown(path: *const c_char, uid: uid_t, gid: gid_t) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn faccessat(
atfd: c_int,
path: *const c_char,
mode: c_int,
flags: c_int,
) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn fchdir(fd: c_int) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn fchown(fd: c_int, uid: uid_t, gid: gid_t) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn fchownat(fd: c_int, path: *const c_char, uid: uid_t, gid: gid_t) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn fpathconf(fd: c_int, name: c_int) -> c_long {
todo!()
}
#[no_mangle]
unsafe extern "C" fn ftruncate(fd: c_int, size: off_t) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn getcwd(buf: *mut c_char, len: usize) -> *mut c_char {
todo!()
}
#[no_mangle]
unsafe extern "C" fn lchown(path: *const c_char, uid: uid_t, gid: gid_t) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn link(path1: *const c_char, path2: *const c_char) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn linkat(
path1fd: c_int,
path1: *const c_char,
path2fd: c_int,
path2: *const c_char,
flags: c_int,
) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn lockf(fd: c_int, op: c_int, len: off_t) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pathconf(path: *const c_char, name: c_int) -> c_long {
todo!()
}
#[no_mangle]
unsafe extern "C" fn readlink(path: *const c_char, buf: *mut c_char, len: usize) -> isize {
todo!()
}
#[no_mangle]
unsafe extern "C" fn readlinkat(
atfd: c_int,
path: *const c_char,
buf: *mut c_char,
len: usize,
) -> isize {
todo!()
}
#[no_mangle]
unsafe extern "C" fn rmdir(path: *const c_char) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn symlink(path1: *const c_char, path2: *const c_char) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn symlinkat(path1: *const c_char, atfd: c_int, path2: *const c_char) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn truncate(path: *const c_char, size: off_t) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn unlink(path: *const c_char) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn unlinkat(atfd: c_int, path: *const c_char, flags: c_int) -> c_int {
todo!()
}

View File

@ -0,0 +1,10 @@
use core::ffi::{c_char, c_int};
#[no_mangle]
unsafe extern "C" fn getopt(
argc: c_int,
argv: *const *mut c_char,
optstring: *const c_char,
) -> c_int {
todo!()
}

View File

@ -0,0 +1,108 @@
use core::{ffi::{c_char, c_int, c_void}, mem::MaybeUninit};
use yggdrasil_rt::{debug_trace, io::{PipeOptions, RawFd, SeekFrom}};
use crate::{
error::{CFdResult, CIntZeroResult, CIsizeResult, COffsetResult, TryFromExt},
headers::sys_types::{off_t, pid_t},
io::{self, raw::RawFile, IntoRawFd, Read, Seek, Write},
};
#[no_mangle]
unsafe extern "C" fn close(fd: c_int) -> CIntZeroResult {
let mut file = RawFile::e_try_from(fd)?;
file.close()?;
CIntZeroResult::SUCCESS
}
#[no_mangle]
unsafe extern "C" fn dup(oldfd: c_int) -> CFdResult {
let oldfile = RawFile::e_try_from(oldfd)?;
let newfd = oldfile.duplicate(None)?.into_raw_fd();
CFdResult::success(newfd)
}
#[no_mangle]
unsafe extern "C" fn dup2(oldfd: c_int, newfd: c_int) -> CFdResult {
let oldfile = RawFile::e_try_from(oldfd)?;
let newfd = RawFd::e_try_from(newfd)?;
let newfd = oldfile.duplicate(Some(newfd))?.into_raw_fd();
CFdResult::success(newfd)
}
#[no_mangle]
unsafe extern "C" fn fdatasync(fd: c_int) -> CIntZeroResult {
debug_trace!("TODO: fdatasync()");
CIntZeroResult::SUCCESS
}
#[no_mangle]
unsafe extern "C" fn fsync(fd: c_int) -> CIntZeroResult {
debug_trace!("TODO: fsync()");
CIntZeroResult::SUCCESS
}
#[no_mangle]
unsafe extern "C" fn isatty(fd: c_int) -> c_int {
debug_trace!("TODO: isatty()");
0
}
#[no_mangle]
unsafe extern "C" fn lseek(fd: c_int, offset: off_t, whence: c_int) -> COffsetResult {
let mut file = RawFile::e_try_from(fd)?;
let seek = SeekFrom::e_try_from((offset, whence))?;
let pos = file.seek(seek)?;
COffsetResult::success(pos)
}
#[no_mangle]
unsafe extern "C" fn pipe(fds: *mut c_int) -> CIntZeroResult {
let fds = core::slice::from_raw_parts_mut(fds, 2);
let (read, write) = io::create_pipe(PipeOptions::empty())?;
fds[0] = read.into_raw().try_into().unwrap();
fds[1] = write.into_raw().try_into().unwrap();
CIntZeroResult::SUCCESS
}
#[no_mangle]
unsafe extern "C" fn pread(fd: c_int, buf: *mut c_void, len: usize, pos: off_t) -> CIsizeResult {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pwrite(fd: c_int, buf: *const c_void, len: usize, pos: off_t) -> CIsizeResult {
todo!()
}
#[no_mangle]
unsafe extern "C" fn sync() {
debug_trace!("TODO: sync()");
}
#[no_mangle]
unsafe extern "C" fn tcgetpgrp(fd: c_int) -> pid_t {
todo!()
}
#[no_mangle]
unsafe extern "C" fn tcsetpgrp(fd: c_int, pgrp: pid_t) -> CIntZeroResult {
todo!()
}
#[no_mangle]
unsafe extern "C" fn write(fd: c_int, buf: *const c_void, count: usize) -> CIsizeResult {
let mut file = RawFile::e_try_from(fd)?;
let data = core::slice::from_raw_parts(buf.cast::<u8>(), count);
let count = file.write(data)?;
CIsizeResult::success(count)
}
#[no_mangle]
unsafe extern "C" fn read(fd: c_int, buf: *mut c_void, count: usize) -> CIsizeResult {
let mut file = RawFile::e_try_from(fd)?;
let data = core::slice::from_raw_parts_mut(buf.cast::<u8>(), count);
let count = file.read(data)?;
CIsizeResult::success(count)
}

View File

@ -0,0 +1,8 @@
#![allow(unused)]
mod exec;
mod fs;
mod getopt;
mod io;
mod process;
mod util;

View File

@ -0,0 +1,126 @@
use core::{
ffi::{c_char, c_int, c_uint},
time::Duration,
};
use yggdrasil_rt::debug_trace;
use crate::{
error::{CIntCountResult, CIntZeroResult},
headers::sys_types::{gid_t, pid_t, uid_t},
process,
};
#[no_mangle]
unsafe extern "C" fn _exit(status: c_int) -> ! {
process::c_exit_immediately(status)
}
#[no_mangle]
unsafe extern "C" fn fork() -> pid_t {
todo!()
}
#[no_mangle]
unsafe extern "C" fn getegid() -> gid_t {
todo!()
}
#[no_mangle]
unsafe extern "C" fn geteuid() -> uid_t {
todo!()
}
#[no_mangle]
unsafe extern "C" fn getgid() -> gid_t {
todo!()
}
#[no_mangle]
unsafe extern "C" fn getgroups(gidsetsize: c_int, grouplist: *mut gid_t) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn getpgid(pid: pid_t) -> pid_t {
todo!()
}
#[no_mangle]
unsafe extern "C" fn getpgrp() -> pid_t {
process::getpgrp()
}
#[no_mangle]
unsafe extern "C" fn getpid() -> pid_t {
process::getpid()
}
#[no_mangle]
unsafe extern "C" fn getppid() -> pid_t {
todo!()
}
#[no_mangle]
unsafe extern "C" fn getsid(pid: pid_t) -> pid_t {
todo!()
}
#[no_mangle]
unsafe extern "C" fn getuid() -> uid_t {
todo!()
}
#[no_mangle]
unsafe extern "C" fn nice(incr: c_int) -> CIntCountResult {
debug_trace!("TODO: nice()");
CIntCountResult::success(0)
}
#[no_mangle]
unsafe extern "C" fn pause() -> CIntZeroResult {
todo!()
}
#[no_mangle]
unsafe extern "C" fn setegid(gid: gid_t) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn seteuid(uid: uid_t) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn setgid(gid: gid_t) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn setpgid(pid: pid_t, pgid: pid_t) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn setpgrp() -> pid_t {
todo!()
}
#[no_mangle]
unsafe extern "C" fn setregid(rgid: gid_t, egid: gid_t) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn setreuid(ruid: uid_t, euid: uid_t) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn setsid() -> pid_t {
todo!()
}
#[no_mangle]
unsafe extern "C" fn setuid(uid: uid_t) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn sleep(seconds: c_uint) -> c_uint {
let duration = Duration::from_secs(seconds.try_into().unwrap());
process::sleep(duration).expect("TODO: sleep failed");
0
}

View File

@ -0,0 +1,45 @@
use core::ffi::{c_char, c_int, c_long, c_uint, c_void};
#[no_mangle]
unsafe extern "C" fn alarm(value: c_uint) -> c_uint {
todo!()
}
#[no_mangle]
unsafe extern "C" fn confstr(name: c_int, buf: *mut c_char, size: usize) -> usize {
todo!()
}
#[no_mangle]
unsafe extern "C" fn gethostid() -> c_long {
todo!()
}
#[no_mangle]
unsafe extern "C" fn gethostname(name: *mut c_char, namelen: usize) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn getlogin() -> *mut c_char {
todo!()
}
#[no_mangle]
unsafe extern "C" fn getlogin_r(name: *mut c_char, namelen: usize) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn swab(from: *const c_void, to: *mut c_void, n: isize) {
todo!()
}
#[no_mangle]
unsafe extern "C" fn sysconf(name: c_int) -> c_long {
todo!()
}
#[no_mangle]
unsafe extern "C" fn ttyname(fd: c_int) -> *mut c_char {
todo!()
}
#[no_mangle]
unsafe extern "C" fn ttyname_r(fd: c_int, buf: *mut c_char, len: usize) -> c_int {
todo!()
}

View File

@ -0,0 +1,230 @@
use core::{mem::MaybeUninit, ops::{Deref, DerefMut}, slice::memchr};
use alloc::{boxed::Box, vec::Vec};
use crate::error::EResult;
use super::{Read, Write};
// Read
enum ReadBufferBacking<'a> {
Owned(Box<[MaybeUninit<u8>]>),
Borrowed(&'a mut [MaybeUninit<u8>])
}
pub struct ReadBuffer<'a> {
data: ReadBufferBacking<'a>,
position: usize,
len: usize
}
// Write
pub trait FileWriter: Write {
fn reset(&mut self);
}
pub struct UnbufferedWriter<W: Write> {
inner: W
}
pub struct FullBufferedWriter<W: Write> {
inner: W,
buffer: Vec<u8>
}
pub struct LineBufferedWriter<W: Write> {
inner: FullBufferedWriter<W>,
need_flush: bool
}
// Read impl
impl<'a> ReadBuffer<'a> {
pub fn owned(capacity: usize) -> Self {
Self {
data: ReadBufferBacking::Owned(Box::new_uninit_slice(capacity)),
position: 0,
len: 0
}
}
pub fn borrowed(data: &'a mut [MaybeUninit<u8>]) -> Self {
Self {
data: ReadBufferBacking::Borrowed(data),
position: 0,
len: 0
}
}
pub fn reset(&mut self) {
self.position = 0;
self.len = 0;
}
pub fn fill_from<R: Read + ?Sized>(&mut self, source: &mut R) -> EResult<&[u8]> {
let buffer = unsafe { self.data.as_slice_mut() };
if self.position == self.len {
let amount = match source.read(buffer) {
EResult::Ok(0) => return EResult::Ok(&buffer[..0]),
EResult::Ok(n) => n,
EResult::Err(err) => return EResult::Err(err),
};
self.position = 0;
self.len = amount;
}
EResult::Ok(&buffer[self.position..self.len])
}
pub fn consume(&mut self, amount: usize) {
self.position = (self.position + amount).min(self.len);
}
}
impl<'a> ReadBufferBacking<'a> {
unsafe fn as_slice_mut(&mut self) -> &mut [u8] {
MaybeUninit::slice_assume_init_mut(self.deref_mut())
}
}
impl<'a> Deref for ReadBufferBacking<'a> {
type Target = [MaybeUninit<u8>];
fn deref(&self) -> &Self::Target {
match self {
Self::Owned(owned) => owned.deref(),
Self::Borrowed(borrowed) => borrowed.deref()
}
}
}
impl<'a> DerefMut for ReadBufferBacking<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
match self {
Self::Owned(owned) => owned.deref_mut(),
Self::Borrowed(borrowed) => borrowed.deref_mut(),
}
}
}
// Write impl
impl<W: Write> UnbufferedWriter<W> {
pub fn new(inner: W) -> Self {
Self { inner }
}
}
impl<W: Write> Write for UnbufferedWriter<W> {
fn write(&mut self, data: &[u8]) -> EResult<usize> {
self.inner.write(data)
}
fn flush(&mut self) -> EResult<()> {
EResult::Ok(())
}
}
impl<W: Write> FileWriter for UnbufferedWriter<W> {
fn reset(&mut self) {}
}
impl<W: Write> FullBufferedWriter<W> {
pub fn with_capacity(inner: W, capacity: usize) -> Self {
Self {
inner,
buffer: Vec::with_capacity(capacity)
}
}
}
impl<W: Write> Write for FullBufferedWriter<W> {
fn write(&mut self, mut data: &[u8]) -> EResult<usize> {
if data.len() + self.buffer.len() >= self.buffer.capacity() {
self.flush()?;
assert_eq!(self.buffer.len(), 0);
}
let len = data.len();
if len >= self.buffer.capacity() {
// Write directly from `data` until it can be fit into the buffer
let (amount, result) = self
.inner
.write_chunked(&data[..len - self.buffer.capacity()]);
// Fail if write fails
result?;
data = &data[amount..];
}
// Store the data in the buffer
assert!(data.len() <= self.buffer.capacity());
self.buffer.extend_from_slice(data);
EResult::Ok(len)
}
fn flush(&mut self) -> EResult<()> {
let (amount, result) = self.inner.write_chunked(&self.buffer);
if amount != 0 {
self.buffer.drain(..amount);
}
result
}
}
impl<W: Write> FileWriter for FullBufferedWriter<W> {
fn reset(&mut self) {
self.buffer.clear();
}
}
impl<W: Write> LineBufferedWriter<W> {
pub fn with_capacity(inner: W, capacity: usize) -> Self {
Self {
inner: FullBufferedWriter::with_capacity(inner, capacity),
need_flush: false
}
}
}
impl<W: Write> Write for LineBufferedWriter<W> {
fn write(&mut self, data: &[u8]) -> EResult<usize> {
if self.need_flush {
self.flush()?;
}
let Some(nl_pos) = memchr::memchr(b'\n', data) else {
return self.inner.write(data);
};
// Write up to the last newline and flush
let amount = self.inner.write(&data[..nl_pos + 1])?;
self.need_flush = true;
if self.flush().is_err() || amount != nl_pos + 1 {
return EResult::Ok(amount);
}
// Write the rest to the buffer
match self.inner.write(&data[nl_pos + 1..]) {
EResult::Ok(amount_to_buffer) => EResult::Ok(amount_to_buffer + amount),
EResult::Err(_) => EResult::Ok(amount),
}
}
fn flush(&mut self) -> EResult<()> {
self.inner.flush()?;
self.need_flush = false;
EResult::Ok(())
}
}
impl<W: Write> FileWriter for LineBufferedWriter<W> {
fn reset(&mut self) {
self.inner.reset();
self.need_flush = false;
}
}

View File

@ -0,0 +1,383 @@
#![allow(non_upper_case_globals)]
use core::{
ops::{Deref, DerefMut},
ptr::{null_mut, NonNull},
};
use alloc::{boxed::Box, collections::btree_set::BTreeSet};
use bitflags::bitflags;
use yggdrasil_rt::{
io::{FileMode, OpenOptions, RawFd, SeekFrom},
path::Path,
};
use crate::{
error::EResult,
headers::errno,
sync::{Mutex, RawMutex},
};
use super::{
buffer::{FileWriter, FullBufferedWriter, LineBufferedWriter, ReadBuffer, UnbufferedWriter},
raw::RawFile,
AsRawFd, FromRawFd, Read, Seek, Write,
};
macro locked_op($self:expr, $op:expr) {{
$self.lock.lock();
let result = unsafe { $op };
unsafe { $self.lock.release() };
result
}}
bitflags! {
pub struct FileFlags: u32 {
const READ = 1 << 0;
const WRITE = 1 << 1;
const EOF = 1 << 2;
const ERROR = 1 << 3;
const BUILTIN = 1 << 16;
}
}
pub trait FileOpenSource {
fn open_with(self, open: OpenOptions) -> EResult<FILE>;
}
pub enum BufferingMode {
Full,
Line,
None,
}
pub enum FileBacking {
File(ManagedFile),
}
pub struct ManagedFile {
inner: RawFile,
reference: bool,
}
pub struct FILE {
lock: RawMutex,
inner: FileBacking,
read_buffer: Option<ReadBuffer<'static>>,
write_buffer: Box<dyn FileWriter>,
flags: FileFlags,
}
// ManagedFile
impl ManagedFile {
pub fn open_at<P: AsRef<Path>>(
at: Option<RawFd>,
pathname: P,
opts: OpenOptions,
mode: FileMode,
) -> EResult<Self> {
let inner = RawFile::open_at(at, pathname, opts, mode)?;
EResult::Ok(Self {
inner,
reference: false,
})
}
pub unsafe fn close(&mut self) -> EResult<()> {
if self.reference {
unreachable!("Cannot close a reference ManagedFile")
}
self.reference = true;
RawFile::close(&mut self.inner)
}
}
impl Deref for ManagedFile {
type Target = RawFile;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl DerefMut for ManagedFile {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl FromRawFd for ManagedFile {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
Self::from(RawFile::from_raw_fd(fd))
}
}
impl From<RawFile> for ManagedFile {
fn from(inner: RawFile) -> Self {
Self {
inner,
reference: false,
}
}
}
impl Drop for ManagedFile {
fn drop(&mut self) {
if !self.reference {
unsafe { self.close().ok() };
}
}
}
// FileBacking
impl FileBacking {
pub unsafe fn make_ref(&self) -> Self {
match self {
Self::File(file) => Self::File(ManagedFile {
inner: RawFile::from_raw_fd(file.as_raw_fd()),
reference: true,
}),
}
}
pub unsafe fn close(&mut self) -> EResult<()> {
match self {
Self::File(file) => file.close(),
}
}
}
impl Write for FileBacking {
fn write(&mut self, data: &[u8]) -> EResult<usize> {
match self {
Self::File(file) => file.write(data),
}
}
fn flush(&mut self) -> EResult<()> {
match self {
Self::File(file) => file.flush(),
}
}
}
impl Read for FileBacking {
fn read(&mut self, data: &mut [u8]) -> EResult<usize> {
match self {
Self::File(file) => file.read(data),
}
}
}
impl Seek for FileBacking {
fn seek(&mut self, pos: SeekFrom) -> EResult<u64> {
match self {
Self::File(file) => file.seek(pos),
}
}
}
// FILE
impl FILE {
unsafe fn from_backing(
backing: FileBacking,
flags: FileFlags,
buffering: BufferingMode,
) -> Self {
let write_buffer = backing.make_ref();
let write_buffer: Box<dyn FileWriter> = match buffering {
BufferingMode::None => Box::new(UnbufferedWriter::new(write_buffer)),
BufferingMode::Line => Box::new(LineBufferedWriter::with_capacity(write_buffer, 1024)),
BufferingMode::Full => Box::new(FullBufferedWriter::with_capacity(write_buffer, 1024)),
};
Self {
lock: RawMutex::new(),
inner: backing,
write_buffer,
read_buffer: None,
flags,
}
}
unsafe fn from_raw_file(file: RawFile, flags: FileFlags) -> Self {
Self::from_managed_file(file.into(), flags)
}
unsafe fn from_managed_file(file: ManagedFile, flags: FileFlags) -> Self {
assert!(!flags.contains(FileFlags::BUILTIN));
let backing = FileBacking::File(file);
Self::from_backing(backing, flags, BufferingMode::Full)
}
unsafe fn new_builtin(fd: RawFd, flags: FileFlags, buffering: BufferingMode) -> Self {
let inner = FileBacking::File(ManagedFile::from_raw_fd(fd));
Self::from_backing(inner, flags, buffering)
}
pub unsafe fn close(mut ptr: NonNull<Self>) -> EResult<()> {
let file = ptr.as_mut();
if file.flags.contains(FileFlags::WRITE) {
file.flush()?;
}
if !file.flags.contains(FileFlags::BUILTIN) {
let mut this = Box::from_raw(file);
let result = FileBacking::close(&mut this.inner);
drop(this);
result
} else {
EResult::Ok(())
}
}
pub fn reset(&mut self) {
if let Some(read_buffer) = self.read_buffer.as_mut() {
read_buffer.reset();
}
self.write_buffer.reset();
self.flags &= !(FileFlags::ERROR | FileFlags::EOF);
}
unsafe fn read_unlocked(&mut self, data: &mut [u8]) -> EResult<usize> {
if !self.flags.contains(FileFlags::READ) {
self.flags |= FileFlags::ERROR;
return EResult::Err(errno::EBADF);
}
if self.read_buffer.is_some() {
todo!()
} else {
match self.inner.read(data) {
EResult::Ok(0) => {
self.flags |= FileFlags::EOF;
EResult::Ok(0)
}
EResult::Ok(len) => EResult::Ok(len),
EResult::Err(err) => {
self.flags |= FileFlags::ERROR;
EResult::Err(err)
}
}
}
}
unsafe fn write_unlocked(&mut self, data: &[u8]) -> EResult<usize> {
if !self.flags.contains(FileFlags::WRITE) {
self.flags |= FileFlags::ERROR;
return EResult::Err(errno::EBADF);
}
match self.write_buffer.write(data) {
EResult::Ok(len) => EResult::Ok(len),
EResult::Err(err) => {
self.flags |= FileFlags::ERROR;
EResult::Err(err)
}
}
}
unsafe fn flush_unlocked(&mut self) -> EResult<()> {
if !self.flags.contains(FileFlags::WRITE) {
self.flags |= FileFlags::ERROR;
return EResult::Err(errno::EBADF);
}
match self.write_buffer.flush() {
EResult::Ok(()) => EResult::Ok(()),
EResult::Err(err) => {
self.flags |= FileFlags::ERROR;
EResult::Err(err)
}
}
}
}
impl Read for FILE {
fn read(&mut self, data: &mut [u8]) -> EResult<usize> {
locked_op!(self, self.read_unlocked(data))
}
}
impl Write for FILE {
fn write(&mut self, data: &[u8]) -> EResult<usize> {
locked_op!(self, self.write_unlocked(data))
}
fn flush(&mut self) -> EResult<()> {
locked_op!(self, self.flush_unlocked())
}
}
impl FileOpenSource for &Path {
fn open_with(self, opts: OpenOptions) -> EResult<FILE> {
let f = ManagedFile::open_at(None, self, opts, FileMode::default_file())?;
let mut flags = FileFlags::empty();
if opts.contains(OpenOptions::READ) {
flags |= FileFlags::READ;
}
if opts.contains(OpenOptions::WRITE) {
flags |= FileFlags::WRITE;
}
EResult::Ok(unsafe { FILE::from_managed_file(f, flags) })
}
}
#[no_mangle]
pub static mut stdout: *mut FILE = null_mut();
#[no_mangle]
pub static mut stderr: *mut FILE = null_mut();
#[no_mangle]
pub static mut stdin: *mut FILE = null_mut();
static OPEN_FILES: Mutex<BTreeSet<NonNull<FILE>>> = Mutex::new(BTreeSet::new());
pub fn deregister_file(file: NonNull<FILE>) -> bool {
OPEN_FILES.lock().remove(&file)
}
pub fn register_file(file: NonNull<FILE>) {
OPEN_FILES.lock().insert(file);
}
pub unsafe fn init() {
let stdin_ = Box::leak(Box::new(FILE::new_builtin(
RawFd::STDIN,
FileFlags::READ,
BufferingMode::Line,
)));
let stdout_ = Box::leak(Box::new(FILE::new_builtin(
RawFd::STDOUT,
FileFlags::WRITE,
BufferingMode::Line,
)));
let stderr_ = Box::leak(Box::new(FILE::new_builtin(
RawFd::STDERR,
FileFlags::WRITE,
BufferingMode::None,
)));
stdin = stdin_;
stdout = stdout_;
stderr = stderr_;
let mut open_files = OPEN_FILES.lock();
open_files.insert(NonNull::from(stdin_));
open_files.insert(NonNull::from(stdout_));
open_files.insert(NonNull::from(stderr_));
}
pub unsafe fn cleanup() {
let mut open_files = OPEN_FILES.lock();
while let Some(file) = open_files.pop_first() {
FILE::close(file).ok();
}
}

View File

@ -0,0 +1,105 @@
use core::{ffi::c_int, mem::MaybeUninit};
use yggdrasil_rt::{io::{PipeOptions, RawFd, SeekFrom}, sys as syscall};
use crate::{
error::{EResult, TryFromExt},
headers::{
errno,
stdio::{SEEK_CUR, SEEK_END, SEEK_SET},
sys_types::off_t,
},
};
pub mod raw;
pub mod managed;
pub mod buffer;
pub trait Write {
fn write(&mut self, data: &[u8]) -> EResult<usize>;
fn flush(&mut self) -> EResult<()>;
fn write_chunked(&mut self, data: &[u8]) -> (usize, EResult<()>) {
let mut pos = 0;
let len = data.len();
while pos < len {
match self.write(data) {
EResult::Ok(0) => todo!(),
EResult::Ok(n) => pos += n,
EResult::Err(err) if err == errno::EINTR => todo!(),
EResult::Err(err) => return (pos, EResult::Err(err)),
}
}
(pos, EResult::Ok(()))
}
fn write_all(&mut self, data: &[u8]) -> EResult<()> {
match self.write(data) {
EResult::Ok(n) if n == data.len() => EResult::Ok(()),
EResult::Ok(_) => todo!(),
EResult::Err(err) => EResult::Err(err)
}
}
}
pub trait Read {
fn read(&mut self, data: &mut [u8]) -> EResult<usize>;
}
pub trait Seek {
fn seek(&mut self, pos: SeekFrom) -> EResult<u64>;
}
pub trait FromRawFd {
unsafe fn from_raw_fd(fd: RawFd) -> Self;
}
pub trait IntoRawFd {
fn into_raw_fd(self) -> RawFd;
}
pub trait AsRawFd {
fn as_raw_fd(&self) -> RawFd;
}
impl TryFromExt<c_int> for RawFd {
fn e_try_from(value: c_int) -> EResult<Self> {
match value {
0.. => EResult::Ok(unsafe { Self::from_raw(value as _) }),
_ => EResult::Err(errno::EBADF),
}
}
}
impl TryFromExt<(off_t, c_int)> for SeekFrom {
fn e_try_from((offset, whence): (off_t, c_int)) -> EResult<Self> {
match whence {
SEEK_SET => {
if offset < 0 {
return EResult::Err(errno::EINVAL);
}
EResult::Ok(Self::Start(offset as _))
}
SEEK_CUR => EResult::Ok(Self::Current(offset)),
SEEK_END => EResult::Ok(Self::End(offset)),
_ => EResult::Err(errno::EINVAL),
}
}
}
pub fn create_pipe(options: PipeOptions) -> EResult<(RawFd, RawFd)> {
let mut fds = MaybeUninit::uninit_array();
unsafe { syscall::create_pipe(&mut fds, options) }?;
let fds = unsafe { MaybeUninit::array_assume_init(fds) };
EResult::Ok((fds[0], fds[1]))
}
pub unsafe fn init() {
managed::init();
}
pub unsafe fn cleanup() {
managed::cleanup();
}

View File

@ -0,0 +1,87 @@
use core::ffi::c_int;
use yggdrasil_rt::{
io::{FileMode, OpenOptions, RawFd, SeekFrom},
path::Path,
sys as syscall,
};
use crate::error::{EResult, TryFromExt};
use super::{AsRawFd, FromRawFd, IntoRawFd, Read, Seek, Write};
#[repr(transparent)]
pub struct RawFile(RawFd);
impl RawFile {
pub fn open_at<P: AsRef<Path>>(
at: Option<RawFd>,
pathname: P,
opts: OpenOptions,
mode: FileMode,
) -> EResult<Self> {
let fd = unsafe { syscall::open(at, pathname.as_ref().as_str(), opts, mode) }?;
EResult::Ok(Self(fd))
}
pub unsafe fn close(&mut self) -> EResult<()> {
unsafe { syscall::close(self.0) }?;
EResult::Ok(())
}
pub fn duplicate(&self, new: Option<RawFd>) -> EResult<Self> {
let fd = unsafe { syscall::clone_fd(self.0, new) }?;
EResult::Ok(Self(fd))
}
}
impl AsRawFd for RawFile {
fn as_raw_fd(&self) -> RawFd {
self.0
}
}
impl FromRawFd for RawFile {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
Self(fd)
}
}
impl IntoRawFd for RawFile {
fn into_raw_fd(self) -> RawFd {
self.0
}
}
impl Write for RawFile {
fn write(&mut self, data: &[u8]) -> EResult<usize> {
let count = unsafe { syscall::write(self.0, data) }?;
EResult::Ok(count)
}
fn flush(&mut self) -> EResult<()> {
EResult::Ok(())
}
}
impl Read for RawFile {
fn read(&mut self, data: &mut [u8]) -> EResult<usize> {
let count = unsafe { syscall::read(self.0, data) }?;
EResult::Ok(count)
}
}
impl Seek for RawFile {
fn seek(&mut self, seek: SeekFrom) -> EResult<u64> {
let mut pos = 0;
unsafe { syscall::seek(self.0, seek, &mut pos) }?;
EResult::Ok(pos)
}
}
impl TryFromExt<c_int> for RawFile {
fn e_try_from(value: c_int) -> EResult<Self> {
let fd = RawFd::e_try_from(value)?;
EResult::Ok(unsafe { Self::from_raw_fd(fd) })
}
}

View File

@ -0,0 +1,62 @@
#![feature(
try_trait_v2,
decl_macro,
maybe_uninit_uninit_array,
maybe_uninit_array_assume_init,
c_variadic,
arbitrary_self_types_pointers,
maybe_uninit_slice,
slice_internals
)]
#![allow(internal_features)]
#![no_std]
extern crate alloc;
use core::{
ffi::{c_char, c_int, c_void},
panic::PanicInfo,
ptr::null,
};
use alloc::boxed::Box;
use yggdrasil_rt::{
debug_trace,
process::{ExitCode, ProcessId, Signal},
};
mod allocator;
mod args;
mod error;
mod io;
mod process;
mod util;
mod sync;
mod types;
pub mod headers;
#[no_mangle]
unsafe extern "C" fn _start(arg: usize) {
extern "C" {
fn main(argc: c_int, argv: *const *const c_char, envp: *const *const c_char) -> c_int;
}
io::init();
let status = main(0, null(), null());
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!()
}
}

View File

@ -0,0 +1,36 @@
use core::{ffi::c_int, time::Duration};
use yggdrasil_rt::{process::ExitCode, sys as syscall};
use crate::{error::EResult, headers::sys_types::pid_t, io};
pub fn getpid() -> pid_t {
let pid = unsafe { syscall::get_pid() };
pid.into_raw().try_into().unwrap()
}
pub fn getpgrp() -> pid_t {
let pgid = unsafe { syscall::get_process_group_id() };
pgid.into_raw().try_into().unwrap()
}
pub fn sleep(duration: Duration) -> EResult<()> {
unsafe { syscall::nanosleep(&duration) }?;
EResult::Ok(())
}
pub fn c_exit_immediately(status: c_int) -> ! {
let code = ExitCode::Exited(status);
unsafe { syscall::exit_process(code) };
}
pub fn c_exit(status: c_int) -> ! {
unsafe {
pre_exit();
c_exit_immediately(status)
}
}
unsafe fn pre_exit() {
io::cleanup();
}

View File

@ -0,0 +1,103 @@
use core::{
cell::UnsafeCell,
ops::{Deref, DerefMut},
sync::atomic::{AtomicU32, Ordering},
};
use yggdrasil_rt::{process::MutexOperation, sys as syscall};
pub struct RawMutex {
value: AtomicU32,
}
pub struct Mutex<T> {
value: UnsafeCell<T>,
inner: RawMutex,
}
pub struct MutexGuard<'a, T> {
lock: &'a Mutex<T>,
}
impl RawMutex {
const UNLOCKED: u32 = 0;
const LOCKED: u32 = 1;
pub const fn new() -> Self {
Self {
value: AtomicU32::new(Self::UNLOCKED),
}
}
pub fn try_lock(&self) -> bool {
self.value
.compare_exchange(
Self::UNLOCKED,
Self::LOCKED,
Ordering::Acquire,
Ordering::Relaxed,
)
.is_ok()
}
pub fn lock(&self) {
loop {
if self.try_lock() {
// Got a lock
return;
}
self.wait(Self::LOCKED);
}
}
pub unsafe fn release(&self) {
if self.value.swap(Self::UNLOCKED, Ordering::Release) == Self::LOCKED {
self.wake();
}
}
fn wait(&self, value: u32) {
unsafe { syscall::mutex(&self.value, &MutexOperation::Wait(value, None)).unwrap() };
}
fn wake(&self) {
unsafe { syscall::mutex(&self.value, &MutexOperation::Wake).unwrap() };
}
}
impl<T> Mutex<T> {
pub const fn new(value: T) -> Self {
Self {
value: UnsafeCell::new(value),
inner: RawMutex::new(),
}
}
pub fn lock(&self) -> MutexGuard<T> {
self.inner.lock();
MutexGuard { lock: self }
}
}
unsafe impl<T> Sync for Mutex<T> {}
impl<T> Deref for MutexGuard<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*self.lock.value.get() }
}
}
impl<T> DerefMut for MutexGuard<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *self.lock.value.get() }
}
}
impl<T> Drop for MutexGuard<'_, T> {
fn drop(&mut self) {
unsafe { self.lock.inner.release() };
}
}

View File

@ -0,0 +1,5 @@
#![allow(non_camel_case_types)]
use core::ffi::c_int;
pub type wchar_t = c_int;

View File

@ -0,0 +1,50 @@
use core::ffi::{c_char, CStr};
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;
}
pub trait PointerStrExt {
unsafe fn ensure_str(self: *const Self) -> &'static str;
unsafe fn ensure_cstr(self: *const Self) -> &'static CStr;
}
impl<T> PointerExt for T {
unsafe fn ensure(self: *const Self) -> &'static Self {
unsafe { self.as_ref().expect("ensure() failed: NULL pointer") }
}
unsafe fn ensure_mut(self: *mut Self) -> &'static mut Self {
unsafe { self.as_mut().expect("ensure_mut() failed: NULL pointer") }
}
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 {
if self.is_null() {
panic!("ensure_slice_mut() failed: NULL pointer");
}
unsafe { core::slice::from_raw_parts_mut(self, len) }
}
}
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")
}
unsafe fn ensure_cstr(self: *const Self) -> &'static CStr {
if self.is_null() {
panic!("ensure_str() failed: NULL pointer");
}
CStr::from_ptr(self)
}
}

View File

@ -37,6 +37,7 @@ pub struct ModuleInfo {
pub enum CargoBuilder<'e> {
Host(bool),
Userspace(&'e BuildEnv),
Ygglibc(&'e BuildEnv),
Kernel(&'e BuildEnv),
Module(&'e BuildEnv, &'e ModuleInfo),
}
@ -78,6 +79,22 @@ impl<'e> CargoBuilder<'e> {
command.arg("--release");
}
}
Self::Ygglibc(env) => {
let target = format!("{:?}-unknown-none", env.arch);
command
.arg(arg)
.arg("-Zbuild-std=core,alloc,compiler_builtins")
.arg(&format!("--target={target}"));
if env.verbose {
command.arg("-v");
}
if env.profile == Profile::Release {
command.arg("--release");
}
}
Self::Host(verbose) => {
command.arg("+nightly").arg(arg);

View File

@ -2,11 +2,18 @@ use std::{
fs::{self, File},
io::BufWriter,
path::{Path, PathBuf},
process::Command,
};
use walkdir::WalkDir;
use crate::{build::cargo::CargoBuilder, check::AllOk, env::BuildEnv, error::Error, util};
use crate::{
build::cargo::CargoBuilder,
check::AllOk,
env::{Arch, BuildEnv},
error::Error,
util,
};
use super::InitrdGenerated;
@ -55,12 +62,54 @@ const PROGRAMS: &[(&str, &str)] = &[
// crypt
("crypt", "bin/crypt"),
("dyn-loader", "libexec/dyn-loader"),
// TODO: proper process for C program builds
("c-test", "c-test"),
];
fn build_test_c_program(env: &BuildEnv) -> Result<(), Error> {
log::info!("Building a test C program");
let mut command = Command::new("clang");
match env.arch {
Arch::aarch64 => todo!(),
Arch::i686 => todo!(),
Arch::x86_64 => {
command.args(["-target", "x86_64-unknown-none"]);
}
}
let ygglibc_target = format!("{:?}-unknown-none", env.arch);
let ygglibc_target_dir = env
.workspace_root
.join("userspace/lib/ygglibc/target")
.join(ygglibc_target)
.join(env.profile.dir());
let target_dir = &env.userspace_output_dir;
command
.args(["-nostdlib", "-ffreestanding", "-static", "-Og", "-ggdb"])
.arg("-I")
.arg("userspace/lib/ygglibc/include")
.arg("-I")
.arg(ygglibc_target_dir.join("include"))
.arg("-o")
.arg(target_dir.join("c-test"))
.arg(ygglibc_target_dir.join("libygglibc.a"))
.arg(env.workspace_root.join("test.c"))
.arg(ygglibc_target_dir.join("libygglibc.a"));
if !command.status()?.success() {
return Err(Error::ExternalCommandFailed);
}
Ok(())
}
fn build_userspace(env: &BuildEnv, _: AllOk) -> Result<(), 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"))?;
CargoBuilder::Ygglibc(env).build(env.workspace_root.join("userspace/lib/ygglibc"))?;
build_test_c_program(env)?;
Ok(())
}