RustForge

foundry

Foundry OS: the operating system built on the Rustbelt kernel

authorGrok Bot <grokbot@localhost>2026-10-31 20:27:26 -0700
commit8c5294825551990c470bee8aad8779c984a3e70b
parent2cf3033360cd036502ae2d0da7ffd980c5407628
treebrowse files at this commit
rbrt: make the runtime a real library crate (imported as ulib)

Programs now depend on rbrt (ulib = { package = "rbrt" }) instead of
including rbrt/ulib.rs with #[path] mod ulib. print!/println!/eprintln!
are #[macro_export]ed, the panic handler and allocator come with the
crate, and ulib::entry!(main) exports a program's main as rbrt_main,
which _start calls. rbrt/README.md explains the pattern.
Mrust/Cargo.lock+19 -0
Mrust/Cargo.toml+4 -4
Mrust/coreutils-rs/Cargo.toml+3 -0
Mrust/coreutils-rs/src/bin/cat.rs+2 -3
Mrust/coreutils-rs/src/bin/cp.rs+2 -3
Mrust/coreutils-rs/src/bin/grep.rs+2 -3
Mrust/coreutils-rs/src/bin/ls.rs+2 -3
Mrust/coreutils-rs/src/bin/mkdir.rs+2 -3
Mrust/coreutils-rs/src/bin/rm.rs+2 -3
Mrust/coreutils-rs/src/bin/sleep.rs+2 -3
Mrust/coreutils-rs/src/bin/touch.rs+2 -3
Mrust/coreutils-rs/src/bin/wc.rs+2 -3
Mrust/init/Cargo.toml+3 -0
Mrust/init/src/main.rs+2 -3
Mrust/net/Cargo.toml+3 -0
Mrust/net/src/bin/fetch.rs+2 -2
Mrust/net/src/bin/host.rs+2 -2
Mrust/net/src/bin/httpd.rs+2 -2
Mrust/net/src/bin/ifconfig.rs+2 -2
Mrust/net/src/bin/ping.rs+2 -2
Arust/rbrt/Cargo.toml+13 -0
Mrust/rbrt/README.md+32 -4
Mrust/rbrt/ulib.rs+30 -7
Mrust/rbsh/Cargo.toml+3 -0
Mrust/rbsh/src/main.rs+2 -3
Mrust/sys/Cargo.toml+3 -0
Mrust/sys/src/bin/getty.rs+2 -3
Mrust/sys/src/bin/random.rs+2 -3
Mrust/sys/src/bin/sysinfo.rs+2 -2
Mrust/sys/src/bin/tile.rs+2 -3

30 files changed, 153 insertions, 69 deletions

@@ -5,19 +5,38 @@ version = 4 [[package]] name = "foundry-coreutils" version = "0.1.0"+dependencies = [+ "rbrt",+]  [[package]] name = "foundry-init" version = "0.1.0"+dependencies = [+ "rbrt",+]  [[package]] name = "foundry-net" version = "0.1.0"+dependencies = [+ "rbrt",+]  [[package]] name = "foundry-rbsh" version = "0.1.0"+dependencies = [+ "rbrt",+]  [[package]] name = "foundry-sys" version = "0.1.0"+dependencies = [+ "rbrt",+]++[[package]]+name = "rbrt"+version = "0.1.0"
@@ -3,12 +3,12 @@ # Every program is a `no_std` binary for the built-in `x86_64-unknown-none` # target (no operating-system standard library), linked as a static ET_EXEC # at Rustbelt's user base 0x80_0040_0000 (see link.ld). The shared runtime-# (syscall wrappers, println!, heap, _start) is rbrt/ulib.rs, which every-# program includes as a module with `#[path = ...] mod ulib;` -- exactly as-# in the kernel repo. Turning rbrt into a real library crate is milestone M1b.+# (syscall wrappers, println!, heap, _start) is the library crate rbrt/+# (imported as `ulib`); see rbrt/README.md. Since M1b it is a real crate,+# no longer a file every program includes with `#[path = ...] mod ulib;`. [workspace] resolver = "3"-members = ["rbsh", "coreutils-rs", "net", "sys", "init"]+members = ["rbrt", "rbsh", "coreutils-rs", "net", "sys", "init"]  [workspace.package] version = "0.1.0"
@@ -8,6 +8,9 @@ publish.workspace = true build = "../rbrt/build.rs" autobins = false +[dependencies]+ulib = { package = "rbrt", path = "../rbrt" }+ [[bin]] name = "ls" path = "src/bin/ls.rs"
@@ -2,9 +2,8 @@ #![no_std] #![no_main] -#[macro_use]-#[path = "../../../rbrt/ulib.rs"]-mod ulib;+extern crate ulib;+ulib::entry!(main);  use ulib::*; 
@@ -5,9 +5,8 @@  extern crate alloc; -#[macro_use]-#[path = "../../../rbrt/ulib.rs"]-mod ulib;+extern crate ulib;+ulib::entry!(main);  use alloc::{format, string::String}; use ulib::*;
@@ -5,9 +5,8 @@ #![no_std] #![no_main] -#[macro_use]-#[path = "../../../rbrt/ulib.rs"]-mod ulib;+extern crate ulib;+ulib::entry!(main);  use ulib::*; 
@@ -10,9 +10,8 @@  extern crate alloc; -#[macro_use]-#[path = "../../../rbrt/ulib.rs"]-mod ulib;+extern crate ulib;+ulib::entry!(main);  use alloc::{format, string::String, vec::Vec}; use ulib::*;
@@ -2,9 +2,8 @@ #![no_std] #![no_main] -#[macro_use]-#[path = "../../../rbrt/ulib.rs"]-mod ulib;+extern crate ulib;+ulib::entry!(main);  use ulib::*; 
@@ -3,9 +3,8 @@ #![no_std] #![no_main] -#[macro_use]-#[path = "../../../rbrt/ulib.rs"]-mod ulib;+extern crate ulib;+ulib::entry!(main);  use ulib::*; 
@@ -3,9 +3,8 @@ #![no_std] #![no_main] -#[macro_use]-#[path = "../../../rbrt/ulib.rs"]-mod ulib;+extern crate ulib;+ulib::entry!(main);  use ulib::*; 
@@ -3,9 +3,8 @@ #![no_std] #![no_main] -#[macro_use]-#[path = "../../../rbrt/ulib.rs"]-mod ulib;+extern crate ulib;+ulib::entry!(main);  use ulib::*; 
@@ -3,9 +3,8 @@ #![no_std] #![no_main] -#[macro_use]-#[path = "../../../rbrt/ulib.rs"]-mod ulib;+extern crate ulib;+ulib::entry!(main);  use ulib::*; 
@@ -8,6 +8,9 @@ publish.workspace = true build = "../rbrt/build.rs" autobins = false +[dependencies]+ulib = { package = "rbrt", path = "../rbrt" }+ [[bin]] name = "init" path = "src/main.rs"
@@ -34,9 +34,8 @@  extern crate alloc; -#[macro_use]-#[path = "../../rbrt/ulib.rs"]-mod ulib;+extern crate ulib;+ulib::entry!(main);  use ulib::*; 
@@ -8,6 +8,9 @@ publish.workspace = true build = "../rbrt/build.rs" autobins = false +[dependencies]+ulib = { package = "rbrt", path = "../rbrt" }+ [[bin]] name = "ifconfig" path = "src/bin/ifconfig.rs"
@@ -13,8 +13,8 @@ #![no_main]  #[macro_use]-#[path = "../../../rbrt/ulib.rs"]-mod ulib;+extern crate ulib;+ulib::entry!(main);  use core::fmt::Write; 
@@ -9,8 +9,8 @@ #![no_main]  #[macro_use]-#[path = "../../../rbrt/ulib.rs"]-mod ulib;+extern crate ulib;+ulib::entry!(main);  fn main() -> i64 {     let Some(name) = ulib::arg(1) else {
@@ -17,8 +17,8 @@ #![no_main]  #[macro_use]-#[path = "../../../rbrt/ulib.rs"]-mod ulib;+extern crate ulib;+ulib::entry!(main);  extern crate alloc; use alloc::format;
@@ -7,8 +7,8 @@ #![no_main]  #[macro_use]-#[path = "../../../rbrt/ulib.rs"]-mod ulib;+extern crate ulib;+ulib::entry!(main);  use ulib::Ip; 
@@ -10,8 +10,8 @@ #![no_main]  #[macro_use]-#[path = "../../../rbrt/ulib.rs"]-mod ulib;+extern crate ulib;+ulib::entry!(main);  const PAYLOAD: usize = 56; // like Linux's ping: 56 + 8 header = 64 bytes 
@@ -0,0 +1,13 @@+[package]+name = "rbrt"+description = "The Foundry OS Rust runtime: syscalls, print!, heap, _start (imported as `ulib`)"+version.workspace = true+edition.workspace = true+license.workspace = true+publish.workspace = true++[lib]+name = "ulib"+path = "ulib.rs"+test = false+bench = false
@@ -5,10 +5,38 @@ raw Rustbelt/Linux syscall wrappers, `print!`/`println!`/`eprintln!`, a `brk` heap (so `Vec` and `String` work), the `_start` entry point that calls the program's `fn main() -> i64`, and the panic handler. -For milestone M1 it is still included as a *module* (`#[path = ...] mod ulib;`),-exactly as in the kernel repo, so the programs compile unchanged. Milestone-M1b turns it into a real library crate (`#[macro_export]` macros,-`#[panic_handler]`, `#[global_allocator]`).+Since milestone M1b it is a real **library crate** (package `rbrt`, imported+under the shorter name `ulib`). A program uses it like this:++```toml+# Cargo.toml of the program's package+[dependencies]+ulib = { package = "rbrt", path = "../rbrt" }+```++```rust+#![no_std]+#![no_main]++#[macro_use]           // brings in print!, println!, eprintln!+extern crate ulib;+ulib::entry!(main);    // tells _start which function is main++use ulib::*;           // the syscall wrappers: open, read, write, ...++fn main() -> i64 {+    println!("hello");+    0                  // the exit code+}+```++How the pieces fit: `_start` (in `ulib.rs`) reads argc/argv/envp from the+stack and calls a function named `rbrt_main`. `ulib::entry!(main)` defines+`rbrt_main` in your program and makes it call your `main`. The panic handler+and the heap allocator (`#[global_allocator]`) also live in the crate, so a+program gets them just by depending on it. (Before M1b, every program pulled+the file in as a module with `#[path = "...ulib.rs"] mod ulib;`; the kernel+repo still does that for its own test programs.)  `build.rs` here is the build script shared by all packages: it adds `-T rust/link.ld` to the link.
@@ -2,14 +2,19 @@ //! eprintln!, a small heap (so `Vec` and `String` work), the `_start` entry //! point and a panic handler. //!-//! Each user program includes it with-//!     #[macro_use] #[path = "ulib.rs"] mod ulib;-//! and defines `fn main() -> i64` (the exit code).+//! It is a library crate: package `rbrt`, imported as `ulib`. Each program+//! depends on it (`ulib = { package = "rbrt", path = "../rbrt" }`) and starts+//! with+//!     #[macro_use] extern crate ulib;+//!     ulib::entry!(main);+//! where `fn main() -> i64` returns the exit code. `entry!` exports that+//! function under the fixed name `rbrt_main`, which `_start` calls. //! //! User programs are built for the `x86_64-unknown-none` target, which has //! SSE/floating point disabled. That matters: the kernel doesn't save SSE //! registers on a context switch. +#![no_std] #![allow(dead_code, unused_macros)]  extern crate alloc;@@ -1091,10 +1096,11 @@ impl Drop for LineWriter {     } } +#[macro_export] macro_rules! print {     ($($arg:tt)*) => {{         use core::fmt::Write;-        let mut out = $crate::ulib::LineWriter::new(1);+        let mut out = $crate::LineWriter::new(1);         let _ = write!(out, $($arg)*);     }}; }@@ -1118,18 +1124,20 @@ impl fmt::Write for Fd {     } } +#[macro_export] macro_rules! eprintln {     ($($arg:tt)*) => {{         use core::fmt::Write;-        let mut out = $crate::ulib::LineWriter::new(2);+        let mut out = $crate::LineWriter::new(2);         let _ = writeln!(out, $($arg)*);     }}; } +#[macro_export] macro_rules! println {     ($($arg:tt)*) => {{         use core::fmt::Write;-        let mut out = $crate::ulib::LineWriter::new(1);+        let mut out = $crate::LineWriter::new(1);         let _ = writeln!(out, $($arg)*);     }}; }@@ -1159,7 +1167,22 @@ extern "C" fn start_rust(sp: *const usize) -> ! {         }         AUXV = p.add(1) as *const [u64; 2];     }-    exit(crate::main())+    unsafe extern "Rust" {+        fn rbrt_main() -> i64; // the program's main, exported by `entry!`+    }+    exit(unsafe { rbrt_main() })+}++/// `ulib::entry!(main);` in a program: export its `fn main() -> i64` as+/// `rbrt_main`, the function `_start` calls.+#[macro_export]+macro_rules! entry {+    ($main:path) => {+        #[unsafe(no_mangle)]+        fn rbrt_main() -> i64 {+            $main()+        }+    }; }  #[panic_handler]
@@ -8,6 +8,9 @@ publish.workspace = true build = "../rbrt/build.rs" autobins = false +[dependencies]+ulib = { package = "rbrt", path = "../rbrt" }+ [[bin]] name = "rbsh" path = "src/main.rs"
@@ -24,9 +24,8 @@  extern crate alloc; -#[macro_use]-#[path = "../../rbrt/ulib.rs"]-mod ulib;+extern crate ulib;+ulib::entry!(main); #[path = "parse.rs"] mod shparse; 
@@ -8,6 +8,9 @@ publish.workspace = true build = "../rbrt/build.rs" autobins = false +[dependencies]+ulib = { package = "rbrt", path = "../rbrt" }+ [[bin]] name = "getty" path = "src/bin/getty.rs"
@@ -20,9 +20,8 @@ #![no_std] #![no_main] -#[macro_use]-#[path = "../../../rbrt/ulib.rs"]-mod ulib;+extern crate ulib;+ulib::entry!(main);  use ulib::*; 
@@ -10,9 +10,8 @@ #![no_std] #![no_main] -#[macro_use]-#[path = "../../../rbrt/ulib.rs"]-mod ulib;+extern crate ulib;+ulib::entry!(main);  use ulib::*; 
@@ -9,8 +9,8 @@ #![no_main]  #[macro_use]-#[path = "../../../rbrt/ulib.rs"]-mod ulib;+extern crate ulib;+ulib::entry!(main);  fn main() -> i64 {     let before = ulib::sysinfo();
@@ -18,9 +18,8 @@ #![no_std] #![no_main] -#[macro_use]-#[path = "../../../rbrt/ulib.rs"]-mod ulib;+extern crate ulib;+ulib::entry!(main);  extern crate alloc; use alloc::vec;