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:
@@ -2,48 +2,69 @@
|
||||
|
||||
## Project Overview
|
||||
|
||||
This is a minimal Rust experiment that builds one executable without Cargo
|
||||
and without linking Rust `core`, `alloc`, or `std`. CMake invokes `rustc`
|
||||
directly. The executable writes `Hello` to stdout and exits.
|
||||
This project is a small Rust common library and runtime built without Cargo.
|
||||
CMake invokes `rustc` directly. The library provides a low-footprint subset
|
||||
of standard-library-like functionality:
|
||||
|
||||
The project currently contains:
|
||||
- `Result` and formatting from `core`
|
||||
- `Vec` and `vec![]` from `alloc`
|
||||
- project-local `print!` and `println!` macros
|
||||
- direct platform stdout and heap calls
|
||||
|
||||
- `main.rs`: the `#![no_core]` Rust program
|
||||
- `CMakeLists.txt`: the direct `rustc` custom command
|
||||
- `.gitignore`: ignores generated build output
|
||||
The project contains:
|
||||
|
||||
## Non-Negotiable No-Core Design
|
||||
- `src/lib.rs`: the `#![no_std]` common library API
|
||||
- `src/io.rs`: the `core::fmt::Write` stdout backend
|
||||
- `src/runtime.rs`: allocator, async polling, and platform entry helpers
|
||||
- `macros/src/lib.rs`: host-side `#[rslibc_macros::main]` attribute macro
|
||||
- `runtime/main.rs`: minimal example entry point
|
||||
- `CMakeLists.txt`: direct `rustc` library, macro, and executable commands
|
||||
|
||||
- Keep `#![no_core]` in `main.rs`.
|
||||
- Do not replace `#![no_core]` with `#![no_std]`; `no_std` still links
|
||||
Rust `core`.
|
||||
- Do not add Cargo files or invoke Cargo.
|
||||
- Do not import or link `core`, `alloc`, or `std`.
|
||||
- Keep the required language-item and marker-trait definitions local to
|
||||
`main.rs` unless the user explicitly asks for a different design.
|
||||
- Use primitive Rust types and explicit platform FFI instead of library
|
||||
helpers.
|
||||
- Keep unsafe code inside small safe wrappers. The public call site for
|
||||
stdout should remain a normal string call such as:
|
||||
## No-Std Design
|
||||
|
||||
- Keep `#![no_std]` in both the library and runtime.
|
||||
- Do not add Rust `std` to the target library or runtime, and do not add
|
||||
Cargo unless explicitly requested.
|
||||
- The host-side procedural macro may use `proc_macro` and host libraries;
|
||||
it is not linked into the target executable.
|
||||
- `core` is allowed and is the foundation for `Result`, formatting, slices,
|
||||
and other basic language-library functionality.
|
||||
- `alloc` is allowed through `extern crate alloc`; it supplies `Vec` and
|
||||
related heap-backed types.
|
||||
- The final runtime must provide its own global allocator and panic handler;
|
||||
`rslibc::entry!` supplies them to an executable.
|
||||
- Keep OS integration in small FFI modules. Do not pull in a general-purpose
|
||||
runtime or C I/O layer for basic output.
|
||||
- `rslibc_macros::main` can wrap `async fn main()` with the small `block_on`
|
||||
executor. It is only a polling loop and does not provide Tokio-style I/O,
|
||||
timers, or a reactor.
|
||||
- Keep unsafe code inside small safe wrappers. Application code should be
|
||||
able to use calls such as:
|
||||
|
||||
```rust
|
||||
write_stdout("Hello\\n");
|
||||
let values = rslibc::vec![1, 2, 3];
|
||||
rslibc::println!("values = {:?}", values);
|
||||
```
|
||||
|
||||
## Output Implementation
|
||||
## Platform Runtime
|
||||
|
||||
- Unix builds call the libc `write` function with file descriptor `1`.
|
||||
- Windows builds call `GetStdHandle`, `WriteFile`, and `ExitProcess`
|
||||
directly through `kernel32`.
|
||||
- The Windows build must not link the C runtime. This keeps the executable
|
||||
small and uses `/NODEFAULTLIB` with the `kernel32` import library.
|
||||
- Preserve the safe `write_stdout(&str)` interface when changing output.
|
||||
- Unix stdout calls libc `write` with file descriptor `1`.
|
||||
- Windows stdout calls `GetStdHandle` and `WriteFile` through `kernel32`.
|
||||
- Windows allocation calls `HeapAlloc` and `HeapFree` through the system
|
||||
heap.
|
||||
- The Windows runtime dynamically links only the platform support needed by
|
||||
`alloc` and the compiler runtime. Do not statically link the full C
|
||||
runtime.
|
||||
- Keep `panic=abort` enabled.
|
||||
|
||||
## Build System
|
||||
|
||||
CMake is configured with `LANGUAGES NONE` and calls `rustc` through an
|
||||
`add_custom_command`. `find_program(RUSTC rustc REQUIRED)` locates the
|
||||
compiler.
|
||||
CMake is configured with `LANGUAGES NONE` and invokes `rustc` through three
|
||||
custom commands:
|
||||
|
||||
1. Compile `macros/src/lib.rs` as a host procedural macro.
|
||||
2. Compile `src/lib.rs` and its modules to `librslibc.rlib`.
|
||||
3. Compile the minimal `runtime/main.rs` and link it against that rlib.
|
||||
|
||||
Configure and build a release executable with Ninja:
|
||||
|
||||
@@ -52,16 +73,28 @@ cmake -S . -B build -GNinja -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build build
|
||||
```
|
||||
|
||||
Build only the host macro with:
|
||||
|
||||
```sh
|
||||
cmake --build build --target rslibc_macros
|
||||
```
|
||||
|
||||
Build only the common library with:
|
||||
|
||||
```sh
|
||||
cmake --build build --target rslibc_library
|
||||
```
|
||||
|
||||
Run it on Unix:
|
||||
|
||||
```sh
|
||||
./build/no_core_write
|
||||
./build/rslibc_example
|
||||
```
|
||||
|
||||
Run it on Windows:
|
||||
|
||||
```powershell
|
||||
.\build\no_core_write.exe
|
||||
.\build\rslibc_example.exe
|
||||
```
|
||||
|
||||
`CMAKE_BUILD_TYPE` is translated explicitly to rustc flags:
|
||||
@@ -70,28 +103,24 @@ Run it on Windows:
|
||||
- `RelWithDebInfo`: `-C opt-level=3 -C debuginfo=2`
|
||||
- Other or unset configurations: `-C opt-level=0 -C debuginfo=2`
|
||||
|
||||
`no_core` is unstable, so CMake passes `RUSTC_BOOTSTRAP=1` to the direct
|
||||
rustc invocation. A nightly compiler may be used instead, but Cargo is
|
||||
still not permitted.
|
||||
|
||||
## Verification
|
||||
|
||||
After changing the source or build configuration, run a clean release build
|
||||
and execute the program:
|
||||
and execute the example:
|
||||
|
||||
```sh
|
||||
rm -rf build
|
||||
cmake -S . -B build -GNinja -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build build
|
||||
./build/no_core_write
|
||||
./build/rslibc_example
|
||||
```
|
||||
|
||||
On Windows, use `Remove-Item -Recurse -Force build` and run
|
||||
`build\\no_core_write.exe` instead. Confirm that stdout contains `Hello`.
|
||||
`build\\rslibc_example.exe` instead. Confirm that stdout contains `Hello`.
|
||||
|
||||
When size matters, inspect the Windows executable size after a release
|
||||
build. Avoid adding runtime libraries, formatting code, or Rust library
|
||||
dependencies.
|
||||
When size matters, inspect the Windows executable after a release build.
|
||||
Avoid adding formatting, allocation, or OS dependencies unless they are
|
||||
needed by the library API.
|
||||
|
||||
## Commit Messages
|
||||
|
||||
@@ -106,7 +135,7 @@ dependencies.
|
||||
|
||||
```text
|
||||
Co-Authored-By: qwen (lmstudio/qwen3.6-27b-mtp): wrote tests + build
|
||||
Co-Authored-By: luna (openai/gpt-5.6-luna): reviewed edge cases
|
||||
Co-Authored-By: luna (openrouter/openai/gpt-5.6-luna): reviewed edge cases
|
||||
```
|
||||
|
||||
## Editing Guidelines
|
||||
@@ -115,5 +144,5 @@ dependencies.
|
||||
- Use four-space indentation in Rust and CMake.
|
||||
- Keep normal text in Markdown within 80 columns where practical.
|
||||
- Do not add speculative abstractions or dependencies.
|
||||
- Update this file when the build workflow, no-core constraints, supported
|
||||
- Update this file when the build workflow, library API, supported
|
||||
platforms, or source layout changes.
|
||||
|
||||
+55
-22
@@ -1,16 +1,17 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
project(no_core_write LANGUAGES NONE)
|
||||
project(rslibc LANGUAGES NONE)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
find_program(RUSTC rustc REQUIRED)
|
||||
|
||||
set(RUST_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/main.rs")
|
||||
set(RUST_BINARY "${CMAKE_CURRENT_BINARY_DIR}/no_core_write${CMAKE_EXECUTABLE_SUFFIX}")
|
||||
set(RUST_MACRO_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/macros/src/lib.rs")
|
||||
set(RUST_MACRO "${CMAKE_CURRENT_BINARY_DIR}/rslibc_macros${CMAKE_SHARED_LIBRARY_SUFFIX}")
|
||||
set(RUST_LIBRARY_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/src/lib.rs")
|
||||
set(RUST_RUNTIME_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/runtime/main.rs")
|
||||
set(RUST_LIBRARY "${CMAKE_CURRENT_BINARY_DIR}/librslibc.rlib")
|
||||
set(RUST_BINARY "${CMAKE_CURRENT_BINARY_DIR}/rslibc_example${CMAKE_EXECUTABLE_SUFFIX}")
|
||||
|
||||
# no_core is an unstable rustc feature. RUSTC_BOOTSTRAP lets this deliberately
|
||||
# minimal example be built with the installed compiler without Cargo or core.
|
||||
set(RUSTC_ENV "RUSTC_BOOTSTRAP=1")
|
||||
set(RUSTC_FLAGS -C panic=abort)
|
||||
set(RUSTC_FLAGS --edition=2021 -C panic=abort)
|
||||
|
||||
# CMAKE_BUILD_TYPE does not automatically affect a custom rustc command.
|
||||
# Pass the corresponding optimization/debug-info settings explicitly.
|
||||
@@ -22,31 +23,63 @@ else()
|
||||
list(APPEND RUSTC_FLAGS -C opt-level=0 -C debuginfo=2)
|
||||
endif()
|
||||
|
||||
# A no_core binary does not get Rust's normal startup/linker setup. Link the
|
||||
# small platform system/runtime library needed by the stdout implementation.
|
||||
# A no_std binary does not get Rust's normal startup or allocator setup.
|
||||
# Use the OS heap and dynamically link only the small platform libraries
|
||||
# needed by this runtime.
|
||||
if (WIN32)
|
||||
# Use Win32 directly instead of the C runtime; this keeps the executable
|
||||
# small while still writing to the console's stdout handle.
|
||||
set(RUSTC_LINK_ARGS
|
||||
-l kernel32
|
||||
-l vcruntime
|
||||
-C
|
||||
"link-args=/NODEFAULTLIB /ENTRY:mainCRTStartup /SUBSYSTEM:CONSOLE /OPT:REF /OPT:ICF"
|
||||
"link-args=/ENTRY:mainCRTStartup /SUBSYSTEM:CONSOLE /OPT:REF /OPT:ICF"
|
||||
)
|
||||
elseif (UNIX)
|
||||
set(RUSTC_LINK_ARGS -C "link-args=-lc")
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${RUST_BINARY}"
|
||||
COMMAND "${CMAKE_COMMAND}" -E env "${RUSTC_ENV}" "${RUSTC}"
|
||||
"${RUST_SOURCE}"
|
||||
--crate-name no_core_write
|
||||
--crate-type bin
|
||||
-o "${RUST_BINARY}"
|
||||
${RUSTC_FLAGS}
|
||||
${RUSTC_LINK_ARGS}
|
||||
DEPENDS "${RUST_SOURCE}"
|
||||
OUTPUT "${RUST_MACRO}"
|
||||
COMMAND "${RUSTC}"
|
||||
"${RUST_MACRO_SOURCE}"
|
||||
--crate-name rslibc_macros
|
||||
--crate-type proc-macro
|
||||
-o "${RUST_MACRO}"
|
||||
--edition=2021
|
||||
-C opt-level=3
|
||||
DEPENDS "${RUST_MACRO_SOURCE}"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
add_custom_target(no_core_write ALL DEPENDS "${RUST_BINARY}")
|
||||
add_custom_command(
|
||||
OUTPUT "${RUST_LIBRARY}"
|
||||
COMMAND "${RUSTC}"
|
||||
"${RUST_LIBRARY_SOURCE}"
|
||||
--crate-name rslibc
|
||||
--crate-type rlib
|
||||
-o "${RUST_LIBRARY}"
|
||||
${RUSTC_FLAGS}
|
||||
DEPENDS
|
||||
"${RUST_LIBRARY_SOURCE}"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/io.rs"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/runtime.rs"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${RUST_BINARY}"
|
||||
COMMAND "${RUSTC}"
|
||||
"${RUST_RUNTIME_SOURCE}"
|
||||
--crate-name rslibc_example
|
||||
--crate-type bin
|
||||
--extern "rslibc=${RUST_LIBRARY}"
|
||||
--extern "rslibc_macros=${RUST_MACRO}"
|
||||
-o "${RUST_BINARY}"
|
||||
${RUSTC_FLAGS}
|
||||
${RUSTC_LINK_ARGS}
|
||||
DEPENDS "${RUST_RUNTIME_SOURCE}" "${RUST_LIBRARY}" "${RUST_MACRO}"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
add_custom_target(rslibc_macros DEPENDS "${RUST_MACRO}")
|
||||
add_custom_target(rslibc_library DEPENDS "${RUST_LIBRARY}")
|
||||
add_custom_target(rslibc_example ALL DEPENDS "${RUST_BINARY}")
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
extern crate proc_macro;
|
||||
|
||||
use proc_macro::{Ident, TokenStream, TokenTree};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn main(_: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let mut output = TokenStream::new();
|
||||
let mut saw_fn = false;
|
||||
let mut renamed = false;
|
||||
let mut is_async = false;
|
||||
|
||||
for token in item {
|
||||
match &token {
|
||||
TokenTree::Ident(identifier)
|
||||
if identifier.to_string() == "async" && !saw_fn =>
|
||||
{
|
||||
is_async = true;
|
||||
}
|
||||
TokenTree::Ident(identifier) if identifier.to_string() == "fn" => {
|
||||
saw_fn = true;
|
||||
}
|
||||
TokenTree::Ident(identifier)
|
||||
if saw_fn && !renamed && identifier.to_string() == "main" =>
|
||||
{
|
||||
output.extend([TokenTree::Ident(Ident::new(
|
||||
"__rslibc_user_main",
|
||||
identifier.span(),
|
||||
))]);
|
||||
renamed = true;
|
||||
continue;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
output.extend([token]);
|
||||
}
|
||||
|
||||
if !renamed {
|
||||
return "compile_error!(\"rslibc::main expects fn main()\");"
|
||||
.parse()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let wrapper = if is_async {
|
||||
"fn main() { rslibc::runtime::block_on(__rslibc_user_main()); } rslibc::entry!(main);"
|
||||
} else {
|
||||
"fn main() { __rslibc_user_main(); } rslibc::entry!(main);"
|
||||
};
|
||||
|
||||
output.extend(TokenStream::from_str(wrapper).unwrap());
|
||||
output
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
#![no_core]
|
||||
#![no_main]
|
||||
#![feature(no_core, lang_items, auto_traits)]
|
||||
#![allow(internal_features)]
|
||||
|
||||
// These three marker traits are the minimum language items needed when core is
|
||||
// not linked. They are compiler contracts, not a replacement for core.
|
||||
#[lang = "pointee_sized"]
|
||||
trait PointeeSized {}
|
||||
|
||||
#[lang = "meta_sized"]
|
||||
trait MetaSized: PointeeSized {}
|
||||
|
||||
#[lang = "sized"]
|
||||
trait Sized: MetaSized {}
|
||||
|
||||
#[lang = "copy"]
|
||||
trait Copy {}
|
||||
|
||||
#[lang = "freeze"]
|
||||
unsafe auto trait Freeze {}
|
||||
|
||||
#[lang = "eh_personality"]
|
||||
extern "C" fn eh_personality() {}
|
||||
|
||||
#[lang = "panic_info"]
|
||||
struct PanicInfo;
|
||||
|
||||
#[lang = "panic_impl"]
|
||||
fn panic_impl(_: &PanicInfo) -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[lang = "panic_cannot_unwind"]
|
||||
fn panic_cannot_unwind() -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[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;
|
||||
fn ExitProcess(status: u32) -> !;
|
||||
}
|
||||
|
||||
// A string slice is a (pointer, length) pair. This keeps the public helper
|
||||
// ergonomic without importing core just to call str::as_ptr and str::len.
|
||||
#[repr(C)]
|
||||
union StringParts<'a> {
|
||||
string: &'a str,
|
||||
bytes: (*const u8, usize),
|
||||
}
|
||||
|
||||
fn write_stdout(message: &str) {
|
||||
let (buffer, length) = unsafe { StringParts { string: message }.bytes };
|
||||
|
||||
unsafe {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
write(1, buffer, length);
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let handle = GetStdHandle(0xffff_fff5); // STD_OUTPUT_HANDLE
|
||||
let mut written: u32 = 0;
|
||||
WriteFile(handle, buffer, length as u32, &mut written, 0 as *mut u8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn main() -> i32 {
|
||||
write_stdout("Hello\n");
|
||||
0
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[no_mangle]
|
||||
pub extern "system" fn mainCRTStartup() -> ! {
|
||||
write_stdout("Hello\n");
|
||||
unsafe { ExitProcess(0) }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
fn print(message: &str) {
|
||||
let _ = rslibc::io::write_str(message);
|
||||
}
|
||||
|
||||
#[rslibc_macros::main]
|
||||
async fn main() {
|
||||
print("Hello\n");
|
||||
}
|
||||
@@ -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