Skip to content

Commit

Permalink
init the commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Azure-stars committed Nov 18, 2024
0 parents commit baf5419
Show file tree
Hide file tree
Showing 9 changed files with 307 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -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"]
53 changes: 53 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -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
17 changes: 17 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[workspace]
resolver = "2"

members = [
"constructor_array",
"constructor_array_macros",
]

[workspace.package]
version = "0.1.0"
authors = ["Youjie Zheng <[email protected]>"]
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"]
16 changes: 16 additions & 0 deletions constructor_array/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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" }
45 changes: 45 additions & 0 deletions constructor_array/README.md
Original file line number Diff line number Diff line change
@@ -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 <https://lld.llvm.org/ELF/start-stop-gc>).


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"]
```
31 changes: 31 additions & 0 deletions constructor_array/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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),
)();
}
}
}
21 changes: 21 additions & 0 deletions constructor_array_macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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
45 changes: 45 additions & 0 deletions constructor_array_macros/README.md
Original file line number Diff line number Diff line change
@@ -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 <https://lld.llvm.org/ELF/start-stop-gc>).


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"]
```
76 changes: 76 additions & 0 deletions constructor_array_macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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()
}
}

0 comments on commit baf5419

Please sign in to comment.