Skip to content

Commit 93c8c5c

Browse files
committed
feat: add no_std compatibility verification package
Signed-off-by: Ho Kim <ho.kim@ulagbulag.io>
1 parent f8c05a3 commit 93c8c5c

6 files changed

Lines changed: 122 additions & 0 deletions

File tree

.github/workflows/main.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,7 @@ jobs:
459459
- uses: ./.github/actions/install-rust
460460
- run: cargo test -p wasmtime-internal-fiber --no-default-features
461461
- run: cargo test -p cranelift-tools --test logged-filetests
462+
- run: CARGO_PROFILE_DEV_PANIC=abort cargo build -p nostd-tests
462463

463464
# Check that Clippy lints are passing.
464465
clippy:

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ members = [
160160
"crates/c-api/artifact",
161161
"crates/environ/fuzz",
162162
"crates/misc/component-async-tests",
163+
"crates/misc/nostd-tests",
163164
"crates/test-programs",
164165
"crates/wasi-preview1-component-adapter",
165166
"crates/wasi-preview1-component-adapter/verify",
@@ -319,6 +320,7 @@ wasmtime-test-util = { path = "crates/test-util" }
319320
byte-array-literals = { path = "crates/wasi-preview1-component-adapter/byte-array-literals" }
320321
pulley-interpreter-fuzz = { path = 'pulley/fuzz' }
321322
component-async-tests = { path = "crates/misc/component-async-tests" }
323+
nostd-tests = { path = "crates/misc/nostd-tests" }
322324

323325
# Bytecode Alliance maintained dependencies:
324326
# ---------------------------

crates/misc/nostd-tests/Cargo.toml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[package]
2+
name = "nostd-tests"
3+
authors = ["The Wasmtime Project Developers"]
4+
license = "Apache-2.0 WITH LLVM-exception"
5+
version = "0.0.0"
6+
edition.workspace = true
7+
rust-version.workspace = true
8+
publish = false
9+
10+
[lib]
11+
crate-type = ["cdylib"]
12+
13+
[lints]
14+
workspace = true
15+
16+
[dependencies]
17+
wasmtime = { workspace = true, features = ["async", "runtime"] }

crates/misc/nostd-tests/README.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
This is a verification package for the CI (Continuous Integration) of
2+
crates that support `#![no_std]` environments. It works by statically
3+
linking packages that need to be checked for `std` library dependencies
4+
and detecting whether duplicate implementations of `panic_impl` exist.
5+
6+
Since `panic_impl` is provided by the std package and multiple
7+
implementations cannot coexist, the compilation will fail with a duplicate
8+
implementation error if even a single package depends on std. If this
9+
package builds successfully with the std feature disabled, it guarantees
10+
that the given feature set supports the `#![no_std]` environment.
11+
12+
Another way to verify this is by checking if the successfully compiled
13+
binary is statically linked. In environments like Linux, you can use the
14+
`ldd` command to ensure that no dynamic libraries are linked.
15+
16+
## Key Technical Terms Used
17+
18+
- `#![no_std]`: The attribute used to indicate the crate does not link the
19+
standard library.
20+
- Static Linking: Combining all necessary libraries into the executable at
21+
compile time.
22+
- `panic_impl`: The language item used to define the strategy for handling
23+
panics.
24+
- Feature Set: The specific combination of Cargo features enabled during
25+
compilation.
26+
27+
## Why this works
28+
29+
In Rust, the `std` crate provides a default panic handler. If you are
30+
building for a bare-metal or restricted environment (no_std), you must
31+
provide your own `#[panic_handler]`. By attempting to link both, you
32+
create a conflict. This "conflict by design" is a simple way to use the
33+
compiler as a gatekeeper to ensure no hidden std dependencies creep into
34+
your embedded or kernel-level code.
35+
36+
## How to use
37+
38+
Since unwinding panics are not supported in a `#![no_std]` environment and
39+
we don't need to worry about panic behavior, you should disable them by
40+
setting the environment variable `CARGO_PROFILE_DEV_PANIC=abort`.
41+
42+
```bash
43+
# Compile
44+
export CARGO_PROFILE_DEV_PANIC='abort'
45+
cargo build -p nostd-tests
46+
47+
# Validate
48+
ldd ./target/debug/libnostd_tests.so |
49+
grep -Posq '^\tstatically linked$'
50+
```

crates/misc/nostd-tests/src/lib.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//! This is a verification package for the CI (Continuous Integration) of
2+
//! crates that support `#![no_std]` environments. It works by statically
3+
//! linking packages that need to be checked for `std` library dependencies
4+
//! and detecting whether duplicate implementations of `panic_impl` exist.
5+
6+
#![deny(clippy::all)]
7+
#![deny(missing_docs)]
8+
#![no_std]
9+
10+
// Below is a list of crates that support the `#![no_std]` environment.
11+
// Declaring a package as "extern crate" ensures that the package is
12+
// statically linked during the build phase. This step also detects `std`
13+
// dependencies.
14+
extern crate wasmtime as _;
15+
16+
/// Boilerplate implementation required for cases where there is no `std`
17+
/// dependency, such as embedded environments.
18+
mod no_std {
19+
#[global_allocator]
20+
static GLOBAL_ALLOC: DummyAlloc = DummyAlloc;
21+
22+
use core::{
23+
alloc::{GlobalAlloc, Layout},
24+
panic,
25+
};
26+
27+
/// A no-op allocator for providing functionality `#[global_allocator]`.
28+
struct DummyAlloc;
29+
30+
unsafe impl GlobalAlloc for DummyAlloc {
31+
unsafe fn alloc(&self, _layout: Layout) -> *mut u8 {
32+
unreachable!()
33+
}
34+
35+
unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {
36+
unreachable!()
37+
}
38+
}
39+
40+
/// A no-op panic handler for providing functionality `#[panic_handler]`.
41+
#[panic_handler]
42+
fn panic_handler(_info: &panic::PanicInfo) -> ! {
43+
loop {}
44+
}
45+
}

0 commit comments

Comments
 (0)