#![no_std] binary with libc on a hosted environment

I'm trying to get a #![no_std] binary up and running with libc on a hosted environment (in my case, macOS aarch64) to simplify later porting to a WASM browser environment.

I got as far as this, but compiling libc and jemalloc doesn't seem to work and throws an error that it's not able to find the libraries to link.

cargo.toml

code
[package]
name = "ccrs"
version = "0.1.0"
edition = "2021"
publish = false

[profile.dev]
panic = "abort"

[profile.release]
panic = "abort"

[dependencies]
regex = { version = "1.9", default-features = false, features = [] }

[target.'cfg(unix)'.dependencies]
libc = { version = "0.2", default-features = false, features = [] }
tikv-jemallocator = { version = "0.5", default-features = false, features = [] }

[features]
default = []
# enable support for the standard library
std = []

.cargo/config.toml

code
[target.'cfg(target_os = "macos")']
rustflags = ["-C", "link-args=-e __start -static -nostartfiles", "-L/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib"]

src/bin/cc.rs

code
// Declaring our binary as `no-std` unconditionally lets us be consistent
// in how we `use` items from `std`, `core` or `alloc`.
#![no_std]
#![no_main]

// We always pull in `std` during tests because it's just easier
// to write tests when you can assume you're on a capable platform.
#[cfg(any(feature = "std", test))]
extern crate std;

// If `std` is absent, we rely on `alloc`.
#[cfg(not(feature = "std"))]
extern crate alloc;

/* == ALLOCATOR == */
// If we're on unix and not using std.
#[cfg(all(unix, not(feature = "std")))]
use tikv_jemallocator::Jemalloc;

#[cfg(all(unix, not(feature = "std")))]
#[global_allocator]
static GLOBAL_ALLOC: Jemalloc = Jemalloc;

/* == PANIC == */
#[cfg(not(feature = "std"))]
#[panic_handler]
pub fn panic(_info: &core::panic::PanicInfo) -> ! {
    unsafe { libc::exit(1) }
}

#[cfg(not(feature = "std"))]
#[no_mangle]
pub extern "C" fn _start() -> ! {
    use alloc::ffi::CString;

    unsafe {
        let s = CString::new("Hello").unwrap();
        libc::printf(s.as_ptr());
    }

    unsafe { libc::exit(0) }
}

Building fails with the following error:

% cargo build
   Compiling libc v0.2.147
   Compiling regex-syntax v0.7.4
   Compiling tikv-jemalloc-sys v0.5.3+5.3.0-patched
error: linking with `cc` failed: exit status: 1
  | ... (removed)
  = note: ld: library not found for -lSystem
          clang: error: linker command failed with exit code 1 (use -v to see invocation)

error: could not compile `libc` (build script) due to previous error
warning: build failed, waiting for other jobs to finish...
error: linking with `cc` failed: exit status: 1
  | ...
  = note: ld: library not found for -lSystem
          clang: error: linker command failed with exit code 1 (use -v to see invocation)


error: could not compile `tikv-jemalloc-sys` (build script) due to previous error

Any hints on how to proceed? Both libc and tikv-jemalloc can be compiled in a no_std environment, officially.

I don't know how to fix your linking problem, but I would not recommend that you continue this approach if your sole requirement is “to simplify later porting to a WASM browser environment”.

If you can write a pure-Rust no_std library, then that's great for WASM because you can expect it to work everywhere. But don't confine yourself to trying to write a no_std binary. wasm32-unknown-unknown is a bit of a weird situation that is not very much like no_std — significant portions of std that aren't about IO, such as HashMap and std::sync::Arc, will in fact work (though in slightly stubbed fashion).

One good and common approach would be to write three crates (in two or three packages):

  • a library package which contains your portable application code,
  • a cdylib library package which depends on the application library, and compiles to WebAssembly with the use of wasm-bindgen, and
  • a binary crate (which may be in the first library package, or may be its own package) which depends on the application library and runs as a command-line or GUI application on the host.

Depending on your needs, the common application library may or may not be no_std. If you can, do, and have more assurance, but if you need HashMap or f32::sin(), don't hesitate to use them.

1 Like

Brilliant idea! Thank you!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.