feat: add no-std common runtime
Add an alloc-backed common library with direct platform I/O. Build a host attribute macro for async entry points and keep runtime boilerplate outside application code. Compile the library, macro, and example directly with rustc through CMake. Co-Authored-By: luna (openrouter/openai/gpt-5.6-luna): runtime work
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
use core::fmt;
|
||||
|
||||
#[cfg(unix)]
|
||||
unsafe extern "C" {
|
||||
fn write(fd: i32, buffer: *const u8, count: usize) -> isize;
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
unsafe extern "system" {
|
||||
fn GetStdHandle(which: u32) -> *mut u8;
|
||||
fn WriteFile(
|
||||
handle: *mut u8,
|
||||
buffer: *const u8,
|
||||
count: u32,
|
||||
written: *mut u32,
|
||||
overlapped: *mut u8,
|
||||
) -> i32;
|
||||
}
|
||||
|
||||
pub struct Stdout;
|
||||
|
||||
impl fmt::Write for Stdout {
|
||||
fn write_str(&mut self, message: &str) -> fmt::Result {
|
||||
unsafe {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
if write(1, message.as_ptr(), message.len()) < 0 {
|
||||
return Err(fmt::Error);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let handle = GetStdHandle(0xffff_fff5); // STD_OUTPUT_HANDLE
|
||||
let mut written: u32 = 0;
|
||||
if WriteFile(
|
||||
handle,
|
||||
message.as_ptr(),
|
||||
message.len() as u32,
|
||||
&mut written,
|
||||
0 as *mut u8,
|
||||
) == 0
|
||||
{
|
||||
return Err(fmt::Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_str(message: &str) -> fmt::Result {
|
||||
let mut stdout = Stdout;
|
||||
fmt::Write::write_str(&mut stdout, message)
|
||||
}
|
||||
|
||||
pub fn print(arguments: fmt::Arguments<'_>) -> fmt::Result {
|
||||
let mut stdout = Stdout;
|
||||
fmt::Write::write_fmt(&mut stdout, arguments)
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
pub mod io;
|
||||
pub mod runtime;
|
||||
|
||||
pub use alloc::vec;
|
||||
pub use alloc::vec::Vec;
|
||||
pub use core::result::Result;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! print {
|
||||
($($arg:tt)*) => {{
|
||||
let _ = $crate::io::print(core::format_args!($($arg)*));
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! println {
|
||||
() => {
|
||||
$crate::print!("\n")
|
||||
};
|
||||
($($arg:tt)*) => {{
|
||||
$crate::print!($($arg)*);
|
||||
$crate::print!("\n");
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! entry {
|
||||
($main:path) => {
|
||||
#[global_allocator]
|
||||
static RSLIBC_ALLOCATOR: $crate::runtime::SystemAllocator =
|
||||
$crate::runtime::SystemAllocator;
|
||||
|
||||
#[panic_handler]
|
||||
fn rslibc_panic(_: &core::panic::PanicInfo<'_>) -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn main() -> i32 {
|
||||
$main();
|
||||
0
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[no_mangle]
|
||||
pub extern "system" fn mainCRTStartup() -> ! {
|
||||
$main();
|
||||
$crate::runtime::exit(0)
|
||||
}
|
||||
};
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
use core::alloc::{GlobalAlloc, Layout};
|
||||
use core::future::Future;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
|
||||
|
||||
#[cfg(unix)]
|
||||
unsafe extern "C" {
|
||||
fn malloc(size: usize) -> *mut u8;
|
||||
fn free(pointer: *mut u8);
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
unsafe extern "system" {
|
||||
fn GetProcessHeap() -> *mut u8;
|
||||
fn HeapAlloc(heap: *mut u8, flags: u32, bytes: usize) -> *mut u8;
|
||||
fn HeapFree(heap: *mut u8, flags: u32, pointer: *mut u8) -> i32;
|
||||
fn ExitProcess(status: u32) -> !;
|
||||
}
|
||||
|
||||
pub struct SystemAllocator;
|
||||
|
||||
// alloc's CString support references the C ABI strlen symbol. Keep this
|
||||
// compatibility shim local so the runtime only needs VCRUNTIME's low-level
|
||||
// allocation helpers instead of the full UCRT.
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn strlen(mut pointer: *const u8) -> usize {
|
||||
let mut length = 0;
|
||||
while *pointer != 0 {
|
||||
pointer = pointer.add(1);
|
||||
length += 1;
|
||||
}
|
||||
length
|
||||
}
|
||||
|
||||
unsafe impl GlobalAlloc for SystemAllocator {
|
||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||
if layout.size() == 0 {
|
||||
return layout.align() as *mut u8;
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
malloc(layout.size())
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
HeapAlloc(GetProcessHeap(), 0, layout.size())
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn dealloc(&self, pointer: *mut u8, layout: Layout) {
|
||||
if layout.size() == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
free(pointer);
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
HeapFree(GetProcessHeap(), 0, pointer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn exit(status: u32) -> ! {
|
||||
unsafe { ExitProcess(status) }
|
||||
}
|
||||
|
||||
fn clone_waker(_: *const ()) -> RawWaker {
|
||||
RawWaker::new(core::ptr::null(), &WAKER_VTABLE)
|
||||
}
|
||||
|
||||
unsafe fn wake_waker(_: *const ()) {}
|
||||
unsafe fn wake_by_ref_waker(_: *const ()) {}
|
||||
unsafe fn drop_waker(_: *const ()) {}
|
||||
|
||||
static WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(
|
||||
clone_waker,
|
||||
wake_waker,
|
||||
wake_by_ref_waker,
|
||||
drop_waker,
|
||||
);
|
||||
|
||||
pub fn block_on<F>(mut future: F)
|
||||
where
|
||||
F: Future<Output = ()>,
|
||||
{
|
||||
let waker = unsafe { Waker::from_raw(clone_waker(core::ptr::null())) };
|
||||
let mut context = Context::from_waker(&waker);
|
||||
let mut future = unsafe { Pin::new_unchecked(&mut future) };
|
||||
|
||||
loop {
|
||||
match future.as_mut().poll(&mut context) {
|
||||
Poll::Ready(()) => return,
|
||||
Poll::Pending => core::hint::spin_loop(),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user