From baf5419e489cbb237458709c71430f2490c09d76 Mon Sep 17 00:00:00 2001 From: Youjie Zheng Date: Mon, 18 Nov 2024 19:37:24 +0800 Subject: [PATCH] init the commit --- .cargo/config.toml | 3 ++ .github/workflows/ci.yml | 53 ++++++++++++++++++++ Cargo.toml | 17 +++++++ constructor_array/Cargo.toml | 16 ++++++ constructor_array/README.md | 45 +++++++++++++++++ constructor_array/src/lib.rs | 31 ++++++++++++ constructor_array_macros/Cargo.toml | 21 ++++++++ constructor_array_macros/README.md | 45 +++++++++++++++++ constructor_array_macros/src/lib.rs | 76 +++++++++++++++++++++++++++++ 9 files changed, 307 insertions(+) create mode 100644 .cargo/config.toml create mode 100644 .github/workflows/ci.yml create mode 100644 Cargo.toml create mode 100644 constructor_array/Cargo.toml create mode 100644 constructor_array/README.md create mode 100644 constructor_array/src/lib.rs create mode 100644 constructor_array_macros/Cargo.toml create mode 100644 constructor_array_macros/README.md create mode 100644 constructor_array_macros/src/lib.rs diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..1c2076e --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,3 @@ +[build] +rustflags = ["-C", "link-arg=-z", "-C", "link-arg=nostart-stop-gc"] +rustdocflags = ["-C", "link-arg=-z", "-C", "link-arg=nostart-stop-gc"] \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..749e2cf --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,53 @@ +name: CI + +on: [push, pull_request] + +jobs: + ci: + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + rust-toolchain: [nightly] + targets: [x86_64-unknown-linux-gnu, x86_64-unknown-none, riscv64gc-unknown-none-elf, aarch64-unknown-none-softfloat] + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + with: + toolchain: ${{ matrix.rust-toolchain }} + components: rust-src, clippy, rustfmt + targets: ${{ matrix.targets }} + - name: Check rust version + run: rustc --version --verbose + - name: Check code format + run: cargo fmt --all -- --check + - name: Clippy + run: cargo clippy --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default + - name: Build + run: cargo build --target ${{ matrix.targets }} --all-features + - name: Unit test + if: ${{ matrix.targets == 'x86_64-unknown-linux-gnu' }} + run: cargo test --target ${{ matrix.targets }} -- --nocapture + + doc: + runs-on: ubuntu-latest + strategy: + fail-fast: false + permissions: + contents: write + env: + default-branch: ${{ format('refs/heads/{0}', github.event.repository.default_branch) }} + RUSTDOCFLAGS: -Zunstable-options --enable-index-page -D rustdoc::broken_intra_doc_links -D missing-docs + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + - name: Build docs + continue-on-error: ${{ github.ref != env.default-branch && github.event_name != 'pull_request' }} + run: cargo doc --no-deps + - name: Deploy to Github Pages + if: ${{ github.ref == env.default-branch }} + uses: JamesIves/github-pages-deploy-action@v4 + with: + single-commit: true + branch: gh-pages + folder: target/doc \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..147aa87 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,17 @@ +[workspace] +resolver = "2" + +members = [ + "constructor_array", + "constructor_array_macros", +] + +[workspace.package] +version = "0.1.0" +authors = ["Youjie Zheng "] +license = "GPL-3.0-or-later OR Apache-2.0 OR MulanPSL-2.0" +homepage = "https://github.com/arceos-org/arceos" +documentation = "https://arceos-org.github.io/constructor_array" +repository = "https://github.com/arceos-org/constructor_array" +keywords = ["arceos", "constructor"] +categories = ["development-tools::procedural-macro-helpers", "no-std"] \ No newline at end of file diff --git a/constructor_array/Cargo.toml b/constructor_array/Cargo.toml new file mode 100644 index 0000000..2604b54 --- /dev/null +++ b/constructor_array/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "constructor_array" +edition = "2021" +description = "Register constructor functions for Rust at complie time under no_std." +documentation = "https://docs.rs/constructor_array" +version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true +readme = "README.md" + +[dependencies] +constructor_array_macros = { path = "../constructor_array_macros" } \ No newline at end of file diff --git a/constructor_array/README.md b/constructor_array/README.md new file mode 100644 index 0000000..a264fcf --- /dev/null +++ b/constructor_array/README.md @@ -0,0 +1,45 @@ +# constructor_array + +Module initialization functions for Rust (like __attribute__((constructor)) in C/C++) under no_std. + + +After registering a constructor function, a function pointer pointing to it will be stored in the `ctor` section. + + +When the program starts, it can call all initialization functions in the `ctor` section in order. + +## Usage + +```rust +use constructor_array::register_ctor; +#[register_ctor] +fn hello_world() { + println!("Hello, world!"); +} + +static MAX_NUM: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0); + +#[register_ctor] +fn set_max_num() { + MAX_NUM.store(20, std::sync::atomic::Ordering::Relaxed); +} + +fn main() { + constructor_array::invoke_ctors(); + println!( + "MAX_NUM: {}", + MAX_NUM.load(std::sync::atomic::Ordering::Relaxed) + ); +} +``` + +## Notes +To avoid section-related symbols being optimized by the compiler, you need to add "-z nostart-stop-gc" to the compile flags (see ). + + +For example, in `.cargo/config.toml`: +```toml +[build] +rustflags = ["-C", "link-arg=-z", "link-arg=nostart-stop-gc"] +rustdocflags = ["-C", "link-arg=-z", "-C", "link-arg=nostart-stop-gc"] +``` \ No newline at end of file diff --git a/constructor_array/src/lib.rs b/constructor_array/src/lib.rs new file mode 100644 index 0000000..5571891 --- /dev/null +++ b/constructor_array/src/lib.rs @@ -0,0 +1,31 @@ +#![no_std] +#![doc = include_str!("../README.md")] + +pub use constructor_array_macros::register_ctor; + +/// Placeholder for the `ctors` section, so that +/// the `__start_ctors` and `__stop_ctors` symbols can be generated. +#[link_section = "ctors"] +#[used] +static _SECTION_PLACE_HOLDER: [u8; 0] = []; + +extern "C" { + fn __start_ctors(); + fn __stop_ctors(); +} + +/// Invoke all constructor functions registered by the `register_ctor` attribute. +/// +/// # Notes +/// Caller should ensure that the `ctor` section will not be disturbed by other sections. +pub fn invoke_ctors() { + for ctor_ptr in (__start_ctors as usize..__stop_ctors as usize) + .step_by(core::mem::size_of::<*const core::ffi::c_void>()) + { + unsafe { + core::mem::transmute::<*const core::ffi::c_void, fn()>( + *(ctor_ptr as *const *const core::ffi::c_void), + )(); + } + } +} diff --git a/constructor_array_macros/Cargo.toml b/constructor_array_macros/Cargo.toml new file mode 100644 index 0000000..559eafa --- /dev/null +++ b/constructor_array_macros/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "constructor_array_macros" +edition = "2021" +description = "Macros for registering constructor functions for Rust under no_std." +documentation = "https://docs.rs/constructor_array_macros" +version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true +readme = "README.md" + +[dependencies] +proc-macro2 = "1.0" +quote = "1.0" +syn = { version = "2.0", features = ["full"] } + +[lib] +proc-macro = true \ No newline at end of file diff --git a/constructor_array_macros/README.md b/constructor_array_macros/README.md new file mode 100644 index 0000000..a264fcf --- /dev/null +++ b/constructor_array_macros/README.md @@ -0,0 +1,45 @@ +# constructor_array + +Module initialization functions for Rust (like __attribute__((constructor)) in C/C++) under no_std. + + +After registering a constructor function, a function pointer pointing to it will be stored in the `ctor` section. + + +When the program starts, it can call all initialization functions in the `ctor` section in order. + +## Usage + +```rust +use constructor_array::register_ctor; +#[register_ctor] +fn hello_world() { + println!("Hello, world!"); +} + +static MAX_NUM: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0); + +#[register_ctor] +fn set_max_num() { + MAX_NUM.store(20, std::sync::atomic::Ordering::Relaxed); +} + +fn main() { + constructor_array::invoke_ctors(); + println!( + "MAX_NUM: {}", + MAX_NUM.load(std::sync::atomic::Ordering::Relaxed) + ); +} +``` + +## Notes +To avoid section-related symbols being optimized by the compiler, you need to add "-z nostart-stop-gc" to the compile flags (see ). + + +For example, in `.cargo/config.toml`: +```toml +[build] +rustflags = ["-C", "link-arg=-z", "link-arg=nostart-stop-gc"] +rustdocflags = ["-C", "link-arg=-z", "-C", "link-arg=nostart-stop-gc"] +``` \ No newline at end of file diff --git a/constructor_array_macros/src/lib.rs b/constructor_array_macros/src/lib.rs new file mode 100644 index 0000000..2bb7d35 --- /dev/null +++ b/constructor_array_macros/src/lib.rs @@ -0,0 +1,76 @@ +//!Macros for registering constructor functions for Rust under no_std, which is like __attribute__((constructor)) in C/C++. +//! +//! **DO NOT** use this crate directly. Use the [constructor_array](https://docs.rs/constructor_array) crate instead. +//! +//! After attching the `register_ctor` macro to the given function, a pointer pointing to it will be stored in the `ctors` section. +//! When the program is loaded, this section will be linked into the binary. The `invoke_ctors` function in the `constructor_array` +//! crate will call all the constructor functions in the `ctors` section. +//! +//! See the documentation of the [constructor_array](https://docs.rs/constructor_array) crate for more details. + +use proc_macro::TokenStream; +use proc_macro2::Span; +use quote::{format_ident, quote}; +use syn::{parse_macro_input, Error, Item}; + +/// Register a constructor function to be called before `main`. +/// +/// The function should have no input arguments and return nothing. +/// +/// See the documentation of the [constructor_array](https://docs.rs/constructor_array) crate for more details. +#[proc_macro_attribute] +pub fn register_ctor(attr: TokenStream, function: TokenStream) -> TokenStream { + if !attr.is_empty() { + return Error::new( + Span::call_site(), + "expect an empty attribute: `#[register_ctor]`", + ) + .to_compile_error() + .into(); + } + + let item: Item = parse_macro_input!(function as Item); + if let Item::Fn(func) = item { + let name = &func.sig.ident; + let name_str = name.to_string(); + let name_ident = format_ident!("_CTOR_{}", name_str); + let output = &func.sig.output; + // Constructor functions should not have any return value. + if let syn::ReturnType::Type(_, _) = output { + return Error::new( + Span::call_site(), + "expect no return value for the constructor function", + ) + .to_compile_error() + .into(); + } + let inputs = &func.sig.inputs; + // Constructor functions should not have any input arguments. + if !inputs.is_empty() { + return Error::new( + Span::call_site(), + "expect no input arguments for the constructor function", + ) + .to_compile_error() + .into(); + } + let block = &func.block; + + quote! { + #[link_section = "ctors"] + #[allow(non_upper_case_globals)] + static #name_ident: extern "C" fn() = #name; + + #[no_mangle] + #[allow(non_upper_case_globals)] + pub extern "C" fn #name() { + #block + } + } + .into() + } else { + Error::new(Span::call_site(), "expect a function to be registered") + .to_compile_error() + .into() + } +}