Build a minimal no_std-binary on all platforms

376 views Asked by At

I need to test a build-dependency in a #[no_std] environment. The environment created by Cargo for build-dependencies is primarily that of the host, not the target, yet there are subtle differences if the to-be-compiled crate is #[no_std]. What I can do as part of integration testing is to create a minimal dummy-crate (which depends on the to-be-tested build-dependency) on disk, and spawn a new Cargo process to build that crate, using Cargo's exit status as the test's fixture. The question is how to build a #[no_std] binary with Cargo without requiring the current installation of rustc to include foreign targets (e.g. arm..., thumb...) or implicitly depend on the platform the tests are currently getting executed from.

The most basic setup to build (not run!) such a minimal #[no_std] crate would be

[package]
name = "nostd"
version = "0.1"
build = true

[build-dependencies]
to_be_tested = ...

[profile.dev]
panic = "abort" # we don't need no panic-handling

... and a main.rs like the following; it needs to be main.rs because libraries don't get to decide if the final crate links to std or not.

#![no_main]
#![no_std]

use core::panic::PanicInfo;

#[panic_handler]
fn panic(_panic: &PanicInfo<'_>) -> ! {
    // won't ever get executed anyway
    loop {}
}

However, this fails to build with

rror: linking with `cc` failed: exit status: 1
  |
  = note: [...]
  = note: ld: warning: no platform load command found in '[...]/symbols.o', assuming: macOS
          ld: Undefined symbols:
            _main, referenced from:
                <initial-undefines>
          clang: error: linker command failed with exit code 1 (use -v to see invocation)

The crux of a missing main-stub is that - afaics - it's signature depends on the current target, and I would like to build said dummy-crate on whatever the build-dependency is currently being tested on. How to do that? Do I need to include platform-specific mains for the major platforms and #[cfg] those? Or can the problem be avoided altogether - the crate does not actually need to run, it only needs to build!

1

There are 1 answers

0
Philippe On

AFAICS, you'll need a no-std target compatible with your hardware. I guess x86_64-unknown-none should work in most cases.

Then, you'll need to add an appropriate entry point in main.rs:

#[no_mangle]
pub unsafe extern "C" fn main() -> ! {
    loop {}
}

cargo build --target x86_64-unknown-none will then execute build.rs and compile successfully.