📝 21 Apr 2024

Last article we were compiling Rust Apps for Apache NuttX RTOS (QEMU Emulator, RISC-V 32-bit). And we hit a baffling error…
$ make
riscv64-unknown-elf-ld: libapps.a
  hello_rust_1.o:
  can't link soft-float modules with double-float modulesLet’s solve the problem! We dive inside the internals of C-to-Rust Interop…
Rust compiles for Soft-Float, NuttX expects Double-Float
(Software vs Hardware Floating-Point)
But Rust doesn’t support Double-Float (by default)
So we create a Rust Custom Target for Double-Float
Rebuild the Rust Core Library for Double-Float
And our Rust App builds OK with NuttX!

Why did our NuttX Build fail? (Pic above)
## Download the NuttX Source Code
$ mkdir nuttx
$ cd nuttx
$ git clone https://github.com/apache/nuttx nuttx
$ git clone https://github.com/apache/nuttx-apps apps
## Configure NuttX for QEMU RISC-V 32-bit
$ cd nuttx
$ tools/configure.sh rv-virt:nsh
$ make menuconfig
## TODO: Enable "Hello Rust Example"
## Build NuttX bundled with the Rust App
$ make
riscv64-unknown-elf-ld: libapps.a
  hello_rust_1.o:
  can't link soft-float modules with double-float modulesGCC Linker failed because it couldn’t link the NuttX Binaries with the Rust Binaries.
Here’s Why: NuttX Build calls GCC Compiler to compile our C Modules…
## Build NuttX Firmware with Tracing Enabled
$ make --trace
...
## GCC compiles `hello_main.c` to `hello.o`
## for RISC-V 32-bit with Double-Float
riscv64-unknown-elf-gcc \
  -march=rv32imafdc \
  -mabi=ilp32d \
  -c \
  hello_main.c \
  -o ...hello.o \
  ...Then NuttX Build calls Rust Compiler to compile our Rust App…
## Build NuttX Firmware with Tracing Enabled
$ make --trace
...
## Rust Compiler compiles `hello_rust_main.rs` to `hello_rust.o`
## for RISC-V 32-bit with Soft-Float
rustc \
  --target riscv32i-unknown-none-elf \
  --edition 2021 \
  --emit obj \
  -g \
  -C panic=abort \
  -O \
  hello_rust_main.rs \
  -o ...hello_rust.oWhich looks like this…

Is there a problem?
Watch closely as we compare GCC Compiler with Rust Compiler (pic above)…
| GCC Compiler | Rust Compiler | 
|---|---|
| riscv64-unknown-elf-gcc  hello_main.c  | rustc  hello_rust_main.rs  | 
| -march  rv32imafdc  | –target  riscv32i-unknown-none-elf  | 
| -mabi  ilp32d  | 
Hmmm the Floats look different…
GCC compiles for (Double-Precision) Hardware Floating-Point…
But Rust Compiler emits Software Floating-Point.
That’s why GCC Linker won’t link the binaries: Hard-Float vs Soft-Float!
| GCC Compiler | Rust Compiler | 
|---|---|
| rv32imafdc | riscv32i | 
| - I: Integer | - I: Integer | 
| - F: Single Hard-Float | (Default is Soft-Float) | 
| - D: Double Hard-Float | (Default is Soft-Float) | 

To verify, we dump the ELF Header for GCC Compiler Output…
## Dump the ELF Header for GCC Output
$ riscv64-unknown-elf-readelf \
  --file-header --arch-specific \
  ../apps/examples/hello/*hello.o                 
## GCC Compiler Output is
## Double-Precision Hardware Floating-Point
Flags: 0x5, RVC, double-float ABIAnd the ELF Header for Rust Compiler Output…
## Dump the ELF Header for Rust Compiler Output
$ riscv64-unknown-elf-readelf \
  --file-header --arch-specific \
  ../apps/examples/hello_rust/*hello_rust.o
## Rust Compiler Output is
## Software Floating-Point
Flags: 0x0Indeed we have a problem: Double-Float and Soft-Float won’t mix! Let’s fix this…

What if we ask Rust Compiler to compile for Double-Float? RV32IMAFDC (Pic above)
Let’s harmonise Rust Compiler with GCC Compiler…
Our Build Target is QEMU Emulator
Which offically supports riscv32gc
“gc” in “riscv32gc” denotes IMAFDC
Hence we could do this…
## Compile `hello_rust_main.rs` to `hello_rust.o`
## for Double-Precision Hardware Floating-Point
rustc \
  --target riscv32gc-unknown-none-elf \
  --edition 2021 \
  --emit obj \
  -g \
  -C panic=abort \
  -O \
  hello_rust_main.rs \
  -o hello_rust.oSorry nope it won’t work…
Error loading target specification: 
  Could not find specification for target "riscv32gc-unknown-none-elf". 
  Run `rustc --print target-list` for a list of built-in targetsThat’s because riscv32gc isn’t a Built-In Rust Target…
## List the Built-In Rust Targets for RISC-V
$ rustup target list | grep riscv
## Nope no riscv32gc!
riscv32i-unknown-none-elf
riscv32imac-unknown-none-elf
riscv32imc-unknown-none-elf
riscv64gc-unknown-linux-gnu
riscv64gc-unknown-none-elf
riscv64imac-unknown-none-elfBut we can create a Custom Rust Target for riscv32gc.
(Coming up next section)
Won’t GCC Compiler have the same problem with Double-Float?
When we list the Built-In GCC Targets…
## List the Built-In Targets for GCC RISC-V.
## ABI means Application Binary Interface
$ riscv64-unknown-elf-gcc --target-help
Supported ABIs (for use with the -mabi= option):
  ilp32 ilp32d ilp32e ilp32f lp64 lp64d lp64fWe see that GCC supports Double-Float: ilp32d
ilp32: 32-bit Int, Long and Pointerd: Double-Precision Hardware Floating-PointThat’s why we saw ilp32d earlier…
## GCC compiles for RISC-V 32-bit (Double-Float)
riscv64-unknown-elf-gcc \
  -march=rv32imafdc \
  -mabi=ilp32d \
  ...We’ll make something similar for Rust Compiler…
(More about Application Binary Interface)

To compile Rust for Double-Float, we need a Custom Target: riscv32gc
How to create the Custom Target?
According to the Official Rust Docs, we shall…
Copy from a Built-In Rust Target
(Like riscv32i)
Tweak it to fit our Custom Rust Target
(Which becomes riscv32gc)
This is how we dump a Built-In Rust Target: riscv32i
## Dump the Built-In Rust Target:
## riscv32i (32-bit RISC-V with Soft-Float)
$ rustc \
  +nightly \
  -Z unstable-options \
  --print target-spec-json \
  --target riscv32i-unknown-none-elf
{
  "arch":        "riscv32",
  "atomic-cas":  false,
  "cpu":         "generic-rv32",
  "data-layout": "e-m:e-p:32:32-i64:64-n32-S128",
  "eh-frame-header":        false,
  "emit-debug-gdb-scripts": false,
  "is-builtin":             true,
  "linker":         "rust-lld",
  "linker-flavor":  "ld.lld",
  "llvm-target":    "riscv32",
  "max-atomic-width":     0,
  "panic-strategy":       "abort",
  "relocation-model":     "static",
  "target-pointer-width": "32"
}That’s the Rust Definition of riscv32i: 32-bit RISC-V with Soft-Float.
We do the same for riscv64gc: 64-bit RISC-V with Double-Float…
## Dump the Built-In Rust Target:
## riscv64gc (64-bit RISC-V with Double-Float)
$ rustc \
  +nightly \
  -Z unstable-options \
  --print target-spec-json \
  --target riscv64gc-unknown-none-elf  
{
  "arch":        "riscv64",
  "code-model":  "medium",
  "cpu":         "generic-rv64",
  "data-layout": "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128",
  "eh-frame-header":        false,
  "emit-debug-gdb-scripts": false,
  "features":      "+m,+a,+f,+d,+c",
  "is-builtin":    true,
  "linker":        "rust-lld",
  "linker-flavor": "ld.lld",
  "llvm-abiname":  "lp64d",
  "llvm-target":   "riscv64",
  "max-atomic-width":     64,
  "panic-strategy":       "abort",
  "relocation-model":     "static",
  "supported-sanitizers": [ "kernel-address" ],
  "target-pointer-width": "64"
}Which has more goodies inside: features, llvm-abiname, …
We’re mashing the Two Targets into a New Target?
Exactly! Based on the above, we create our Rust Custom Target: riscv32gc-unknown-none-elf.json
{
  "arch":        "riscv32",
  "cpu":         "generic-rv32",
  "data-layout": "e-m:e-p:32:32-i64:64-n32-S128",
  "eh-frame-header":        false,
  "emit-debug-gdb-scripts": false,
  "features":      "+m,+a,+f,+d,+c",
  "linker":        "rust-lld",
  "linker-flavor": "ld.lld",
  "llvm-abiname":  "ilp32d",
  "llvm-target":   "riscv32",
  "max-atomic-width":     0,
  "panic-strategy":       "abort",
  "relocation-model":     "static",
  "target-pointer-width": "32"
}Which is riscv32i plus these changes…
Remove “is-builtin”: true
(It’s a Custom Target, not Built-In)
Remove “atomic-cas”: false
Add “features”: “+m,+a,+f,+d,+c”
Add “llvm-abiname”: “ilp32d”
In Summary: We spliced Two Built-In Targets into Custom Target riscv32gc…
| riscv32i  (Built-In)  | riscv64gc  (Built-In)  | riscv32gc  (Custom)  | |
|---|---|---|---|
| arch | riscv32 | riscv64 | riscv32 | 
| atomic-cas | false | ||
| cpu | generic-rv32 | generic-rv64 | generic-rv32 | 
| data-layout | e-m:e-p:32… | e-m:e-p:64… | e-m:e-p:32… | 
| features | +m,+a,+f,+d,+c | +m,+a,+f,+d,+c | |
| is-builtin | true | true | |
| llvm-abiname | lp64d | ilp32d | |
| llvm-target | riscv32 | riscv64 | riscv32 | 
| max-atomic-width | 64 | 0 | |
| target-pointer-width | 32 | 64 | 32 | 
Are we ready to rebuild with Double-Float?
Not quite, we’re not done with the System Library…
## Rust Compiler fails to compile with our Custom Target `riscv32gc`
$ rustc \
  --target riscv32gc-unknown-none-elf.json \
  --edition 2021 \
  --emit obj \
  -g \
  -C panic=abort \
  -O \
  hello_rust_main.rs \
  -o hello_rust.o
## That's because Rust Core Library for `riscv32gc` is missing
error[E0463]: can't find crate for `core`Why? Remember…
GCC Compiler supports Double-Float…
Because it’s bundled with C Standard Library for Double-Float
Thus Rust Compiler will support Double-Float…
Only when it has the Rust Core Library for Double-Float
And the Rust Core Library comes from?
We call Rust Compiler to build the Rust Core Library for Double-Float riscv32gc…
## Download our Custom Target for `riscv32gc`
rm -f riscv32gc-unknown-none-elf.json
wget https://raw.githubusercontent.com/lupyuen/nuttx-rust-app/main/riscv32gc-unknown-none-elf.json
## Custom Target needs Nightly Build of Rust Compiler
rustup override set nightly
rustup component add rust-src --toolchain nightly
## Verify our Custom Target, make sure it's OK
rustc \
  --print cfg \
  --target riscv32gc-unknown-none-elf.json
## `cargo build` requires a Rust Project, so we create an empty one.
rm -rf app
cargo new app
## Build the Rust Core Library for `riscv32gc`
## Include the `alloc` library, which will support Heap Memory in future.
## Ignore the error: `can't find crate for std`
pushd app
cargo build \
  -Zbuild-std=core,alloc \
  --target ../riscv32gc-unknown-none-elf.json \
  || true
popdRust Core Library for Double-Float riscv32gc is done!
## Show the Rust Core Library for `riscv32gc`
$ ls app/target/riscv32gc-unknown-none-elf/debug/deps 
alloc-254848389e7e2c53.d
app-cf88b81a5fca23b3.d
compiler_builtins-d5922d64507adf16.d
core-ec2ec78e26b8c830.d
liballoc-254848389e7e2c53.rlib
liballoc-254848389e7e2c53.rmeta
libcompiler_builtins-d5922d64507adf16.rlib
libcompiler_builtins-d5922d64507adf16.rmeta
libcore-ec2ec78e26b8c830.rlib
libcore-ec2ec78e26b8c830.rmeta
librustc_std_workspace_core-3cc5bcc9f701a6e7.rlib
librustc_std_workspace_core-3cc5bcc9f701a6e7.rmeta
rustc_std_workspace_core-3cc5bcc9f701a6e7.dNow we rebuild our Rust App with the Custom Target (linked to our Rust Core Library)…
## Compile our Rust App with Rust Core Library for `riscv32gc`
## We changed the Target to `riscv32gc-unknown-none-elf.json`
## TODO: Change `../apps` to the NuttX Apps Folder
rustc \
  --target riscv32gc-unknown-none-elf.json \
  --edition 2021 \
  --emit obj \
  -g \
  -C panic=abort \
  -O \
  ../apps/examples/hello_rust/hello_rust_main.rs \
  -o ../apps/examples/hello_rust/*hello_rust.o \
  \
  -C incremental=app/target/riscv32gc-unknown-none-elf/debug/incremental \
  -L dependency=app/target/riscv32gc-unknown-none-elf/debug/deps \
  -L dependency=app/target/debug/deps \
  --extern noprelude:alloc=`ls app/target/riscv32gc-unknown-none-elf/debug/deps/liballoc-*.rlib` \
  --extern noprelude:compiler_builtins=`ls app/target/riscv32gc-unknown-none-elf/debug/deps/libcompiler_builtins-*.rlib` \
  --extern noprelude:core=`ls app/target/riscv32gc-unknown-none-elf/debug/deps/libcore-*.rlib` \
  -Z unstable-options(We’ll talk about the loooong options)
Are we Double-Floating yet?
Yep we have a Yummy Double-Float with 2 scoops of ice cream…
## Dump the ELF Header of our Compiled Rust App
## TODO: Change `../apps` to the NuttX Apps Folder
$ riscv64-unknown-elf-readelf \
  --file-header --arch-specific \
  ../apps/examples/hello_rust/*hello_rust.o
## We have Double-Float `riscv32gc` yay!
Flags: 0x5, RVC, double-float ABIHow did we get the Rust Compiler Options?
We copied the above options from cargo build -v…
rustc vs cargo build: What’s the diff?
rustc is the Rust Compiler that compiles Rust Programs to Rust Binaries
(Works like GCC Compiler)
cargo build wraps around rustc for Multi-Step Builds
We could have called rustc for building the Rust Core Library. But it will be a bunch of steps with many many options.

We compiled our Rust App with Double-Float riscv32gc…
Is our NuttX Build hunky dory now?
Yep NuttX builds OK now! GCC Compiler and Rust Compiler are harmonised to Double-Float…
## Copy the Rust Binary that will be linked with NuttX
## TODO: Change `../apps` to the NuttX Apps Folder
cp \
  ../apps/examples/hello_rust/*hello_rust.o \
  ../apps/examples/hello_rust/*hello_rust_1.o
## NuttX should link correctly now.
## TODO: Change `../nuttx` to the NuttX Kernel Folder
pushd ../nuttx
make
popdWe boot NuttX in QEMU Emulator for 32-bit RISC-V…
## For macOS:
brew install qemu
## For Debian and Ubuntu:
sudo apt install qemu-system-riscv32
## Boot NuttX in QEMU RISC-V (32-bit)
## TODO: Change `../nuttx` to the NuttX Kernel Folder
pushd ../nuttx
qemu-system-riscv32 \
  -semihosting \
  -M virt,aclint=on \
  -cpu rv32 \
  -bios none \
  -kernel nuttx \
  -nographic
popdOur Rust App works wonderfully on NuttX! (Pic below)
NuttShell (NSH) NuttX-12.4.0-RC0
nsh> hello_rust
Hello, Rust!!
## Exit QEMU: Press `Ctrl-A` then `x`Phew so much work to build a tiny Rust App?
Yeah. And integrating this into the NuttX Makefiles will be challenging.
(How would Linux Kernel handle Custom Rust Targets?)
(More about Hard-Float Targets in RISC-V)

From 32-bit to 64-bit: We tried compiling our Rust App for 64-bit RISC-V QEMU…
## Build NuttX for QEMU RISC-V 64-bit 
$ tools/configure.sh rv-virt:nsh64
$ make menuconfig
## TODO: Enable "Hello Rust Example"
$ make
RUSTC:  hello_rust_main.rs error: Error loading target specification: 
  Could not find specification for target "riscv64i-unknown-none-elf". 
  Run `rustc --print target-list` for a list of built-in targetsBut Rust Compiler says that riscv64i isn’t a valid Rust Target for 64-bit RISC-V.
Exercise for the Reader:
Is riscv64i the correct target for QEMU?
[10 points]
How should we Fix the Build?
[10 points]
Do we need a Custom Target?
(Hint: Answer is printed in this article somewhere)
[10 points]
Will it run on Ox64 BL808 SBC?
[10 points]

Today we learnt a bit more about C-to-Rust Interop (pic above)…
NuttX failed to link our Rust App because Rust compiles for Soft-Float, NuttX expects Double-Float
(Software vs Hardware Floating-Point)
But Rust doesn’t support Double-Float
(Built-In Target doesn’t exist for 32-bit RISC-V)
So we created a Rust Custom Target for Double-Float: RISCV32GC
(By mashing up RISCV32I and RISCV64GC)
We rebuilt the Rust Core Library for Double-Float
(With cargo build)
And our Rust App builds OK with NuttX
(Runs perfectly on QEMU Emulator for RISC-V)
Check out the new article…
Many Thanks to my GitHub Sponsors (and the awesome NuttX Community) for supporting my work! This article wouldn’t have been possible without your support.
Got a question, comment or suggestion? Create an Issue or submit a Pull Request here…
lupyuen.github.io/src/rust4.md

Follow these steps to build NuttX for QEMU Emulator (32-bit RISC-V) bundled with our Rust App (pic above)…
#!/usr/bin/env bash
#  Build NuttX for QEMU RISC-V 32-bit with Rust App
## TODO: Set PATH
export PATH="$HOME/xpack-riscv-none-elf-gcc-13.2.0-2/bin:$PATH"
set -e  #  Exit when any command fails
set -x  #  Echo commands
## Build NuttX
function build_nuttx {
  ## Go to NuttX Folder
  pushd ../nuttx
  ## Build NuttX
  make -j 8
  ## Return to previous folder
  popd
}
## Build Rust App for QEMU RISC-V 32-bit with Rust Custom Target
function build_rust_riscv32 {
  ## Go to NuttX Folder
  pushd ../nuttx
  ## Download our Custom Target for `riscv32gc`
  rm -f riscv32gc-unknown-none-elf.json
  wget https://raw.githubusercontent.com/lupyuen/nuttx-rust-app/main/riscv32gc-unknown-none-elf.json
  ## Custom Target needs Nightly Build of Rust Compiler
  rustup override set nightly
  rustup component add rust-src --toolchain nightly
  ## Verify our Custom Target, make sure it's OK
  rustc \
    --print cfg \
    --target riscv32gc-unknown-none-elf.json
  ## `cargo build` requires a Rust Project, so we create an empty one.
  rm -rf app
  cargo new app
  ## Build the Rust Core Library for `riscv32gc`
  ## Include the `alloc` library, which will support Heap Memory in future.
  ## Ignore the error: `can't find crate for std`
  pushd app
  cargo build \
    -Zbuild-std=core,alloc \
    --target ../riscv32gc-unknown-none-elf.json \
    || true
  popd
  ## Compile our Rust App with Rust Core Library for `riscv32gc`
  ## We changed the Target to `riscv32gc-unknown-none-elf.json`
  rustc \
    --target riscv32gc-unknown-none-elf.json \
    --edition 2021 \
    --emit obj \
    -g \
    -C panic=abort \
    -O \
    ../apps/examples/hello_rust/hello_rust_main.rs \
    -o ../apps/examples/hello_rust/*hello_rust.o \
    \
    -C incremental=app/target/riscv32gc-unknown-none-elf/debug/incremental \
    -L dependency=app/target/riscv32gc-unknown-none-elf/debug/deps \
    -L dependency=app/target/debug/deps \
    --extern noprelude:alloc=`ls app/target/riscv32gc-unknown-none-elf/debug/deps/liballoc-*.rlib` \
    --extern noprelude:compiler_builtins=`ls app/target/riscv32gc-unknown-none-elf/debug/deps/libcompiler_builtins-*.rlib` \
    --extern noprelude:core=`ls app/target/riscv32gc-unknown-none-elf/debug/deps/libcore-*.rlib` \
    -Z unstable-options
  ## NuttX Build need us to copy hello_rust.o to hello_rust_1.o
  cp \
    ../apps/examples/hello_rust/*hello_rust.o \
    ../apps/examples/hello_rust/*hello_rust_1.o
  ## Return to previous folder
  popd
}
## Configure the NuttX Build
make distclean
tools/configure.sh rv-virt:nsh
## Enable the Hello Rust App
kconfig-tweak --enable CONFIG_EXAMPLES_HELLO_RUST
## Update the Kconfig Dependencies
make olddefconfig
## Build NuttX. Ignore the error: `can't link soft-float modules with double-float modules`
build_nuttx || true
## Build the Rust App with Custom Target
build_rust_riscv32
## Link the Rust App with NuttX
build_nuttx
## Show the size
riscv-none-elf-size nuttx
## Export the Binary Image to nuttx.bin
riscv-none-elf-objcopy \
  -O binary \
  nuttx \
  nuttx.bin
## Copy the config
cp .config nuttx.config
## Dump the NuttX Kernel Disassembly to nuttx.S
riscv-none-elf-objdump \
  --syms --source --reloc --demangle --line-numbers --wide \
  --debugging \
  nuttx \
  >nuttx.S \
  2>&1
## Dump the Rust App Disassembly to hello_rust_1.S
riscv-none-elf-objdump \
  --syms --source --reloc --demangle --line-numbers --wide \
  --debugging \
  ../apps/examples/hello_rust/*1.o \
  >hello_rust_1.S \
  2>&1
## Start the emulator
qemu-system-riscv32 \
  -semihosting \
  -M virt,aclint=on \
  -cpu rv32 \
  -kernel nuttx \
  -nographic \
  -bios noneHow did we get the Rust Compiler Options for riscv32gc?
Earlier we compiled our Rust App with Rust Core Library for riscv32gc…
And we saw these Rust Compiler Options…
## Compile our Rust App with Rust Core Library for `riscv32gc`
## We changed the Target to `riscv32gc-unknown-none-elf.json`
## TODO: Change `../apps` to the NuttX Apps Folder
rustc \
  --target riscv32gc-unknown-none-elf.json \
  --edition 2021 \
  --emit obj \
  -g \
  -C panic=abort \
  -O \
  ../apps/examples/hello_rust/hello_rust_main.rs \
  -o ../apps/examples/hello_rust/*hello_rust.o \
  \
  -C incremental=app/target/riscv32gc-unknown-none-elf/debug/incremental \
  -L dependency=app/target/riscv32gc-unknown-none-elf/debug/deps \
  -L dependency=app/target/debug/deps \
  --extern noprelude:alloc=`ls app/target/riscv32gc-unknown-none-elf/debug/deps/liballoc-*.rlib` \
  --extern noprelude:compiler_builtins=`ls app/target/riscv32gc-unknown-none-elf/debug/deps/libcompiler_builtins-*.rlib` \
  --extern noprelude:core=`ls app/target/riscv32gc-unknown-none-elf/debug/deps/libcore-*.rlib` \
  -Z unstable-optionsThe above options were copied from cargo build -v, here’s how…
Remember we ran cargo build to compile the Rust Core Library?
## Download our Custom Target for `riscv32gc`
rm -f riscv32gc-unknown-none-elf.json
wget https://raw.githubusercontent.com/lupyuen/nuttx-rust-app/main/riscv32gc-unknown-none-elf.json
## Custom Target needs Nightly Build of Rust Compiler
rustup override set nightly
rustup component add rust-src --toolchain nightly
## Verify our Custom Target, make sure it's OK
rustc \
  --print cfg \
  --target riscv32gc-unknown-none-elf.json
## `cargo build` requires a Rust Project, so we create an empty one.
rm -rf app
cargo new app
## Build the Rust Core Library for `riscv32gc`
## Include the `alloc` library, which will support Heap Memory in future.
## Ignore the error: `can't find crate for std`
pushd app
cargo build \
  -Zbuild-std=core,alloc \
  --target ../riscv32gc-unknown-none-elf.json \
  || true
popdcargo build will call rustc with a whole bunch of options.
We switched it to cargo build -v, which will dump the rustc options.
Hence we see the options that will compile a Rust App with our Rust Core Library for riscv32gc…
(TODO: Will these options change in future versions of cargo?)
## Build the Rust Core Library for `riscv32gc`
## And the Empty Rust Project for `riscv32gc`
## `-v` will dump the `rustc` options
$ cargo build -v \
  -Zbuild-std=core,alloc \
  --target ../riscv32gc-unknown-none-elf.json
   Compiling compiler_builtins v0.1.101
   Compiling core v0.0.0 ($HOME/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/library/core)
     ## Generate the Rust Build Script for `riscv32gc`
     Running `$HOME/.rustup/toolchains/nightly-x86_64-apple-darwin/bin/rustc
       --crate-name build_script_build
       --edition=2018 
       $HOME/.cargo/registry/src/index.crates.io-6f17d22bba15001f/compiler_builtins-0.1.101/build.rs
       --error-format=json
       --json=diagnostic-rendered-ansi,artifacts,future-incompat
       --diagnostic-width=94
       --crate-type bin
       --emit=dep-info,link
       -C embed-bitcode=no
       -C debuginfo=2
       -C split-debuginfo=unpacked
       --cfg 'feature="compiler-builtins"'
       --cfg 'feature="core"'
       --cfg 'feature="default"'
       --cfg 'feature="rustc-dep-of-std"'
       -C metadata=9bd0bac7535b33a8
       -C extra-filename=-9bd0bac7535b33a8
       --out-dir $HOME/riscv/nuttx-rust-app/app/target/debug/build/compiler_builtins-9bd0bac7535b33a8
       -Z force-unstable-if-unmarked
       -L dependency=$HOME/riscv/nuttx-rust-app/app/target/debug/deps
       --cap-lints allow`
     ## Build the Rust Core Library for `riscv32gc`
     Running `$HOME/.rustup/toolchains/nightly-x86_64-apple-darwin/bin/rustc
       --crate-name core
       --edition=2021 
       $HOME/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/lib.rs
       --error-format=json
       --json=diagnostic-rendered-ansi,artifacts,future-incompat
       --diagnostic-width=94
       --crate-type lib
       --emit=dep-info,metadata,link
       -C embed-bitcode=no
       -C debuginfo=2
       -C metadata=d271c6ebb87f9b41
       -C extra-filename=-d271c6ebb87f9b41
       --out-dir $HOME/riscv/nuttx-rust-app/app/target/riscv32gc-unknown-none-elf/debug/deps
       --target $HOME/riscv/nuttx-rust-app/riscv32gc-unknown-none-elf.json
       -Z force-unstable-if-unmarked
       -L dependency=$HOME/riscv/nuttx-rust-app/app/target/riscv32gc-unknown-none-elf/debug/deps
       -L dependency=$HOME/riscv/nuttx-rust-app/app/target/debug/deps
       --cap-lints allow`
     Running `$HOME/riscv/nuttx-rust-app/app/target/debug/build/compiler_builtins-9bd0bac7535b33a8/build-script-build`
     
   Compiling rustc-std-workspace-core v1.99.0 ($HOME/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/library/rustc-std-workspace-core)
     ## Build the Rust Workspace Core for `riscv32gc`
     Running `$HOME/.rustup/toolchains/nightly-x86_64-apple-darwin/bin/rustc
       --crate-name rustc_std_workspace_core
       --edition=2021 
       $HOME/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/library/rustc-std-workspace-core/lib.rs
       --error-format=json
       --json=diagnostic-rendered-ansi,artifacts,future-incompat
       --diagnostic-width=94
       --crate-type lib
       --emit=dep-info,metadata,link
       -C embed-bitcode=no
       -C debuginfo=2
       -C metadata=52e0df2b2cc19b6e
       -C extra-filename=-52e0df2b2cc19b6e
       --out-dir $HOME/riscv/nuttx-rust-app/app/target/riscv32gc-unknown-none-elf/debug/deps
       --target $HOME/riscv/nuttx-rust-app/riscv32gc-unknown-none-elf.json
       -Z force-unstable-if-unmarked
       -L dependency=$HOME/riscv/nuttx-rust-app/app/target/riscv32gc-unknown-none-elf/debug/deps
       -L dependency=$HOME/riscv/nuttx-rust-app/app/target/debug/deps
       --extern core=$HOME/riscv/nuttx-rust-app/app/target/riscv32gc-unknown-none-elf/debug/deps/libcore-d271c6ebb87f9b41.rmeta
       --cap-lints allow`
     ## Build the Rust Compiler Builtins for `riscv32gc`
     Running `$HOME/.rustup/toolchains/nightly-x86_64-apple-darwin/bin/rustc
       --crate-name compiler_builtins
       --edition=2018 
       $HOME/.cargo/registry/src/index.crates.io-6f17d22bba15001f/compiler_builtins-0.1.101/src/lib.rs
       --error-format=json
       --json=diagnostic-rendered-ansi,artifacts,future-incompat
       --diagnostic-width=94
       --crate-type lib
       --emit=dep-info,metadata,link
       -C embed-bitcode=no
       -C debuginfo=2
       --cfg 'feature="compiler-builtins"'
       --cfg 'feature="core"'
       --cfg 'feature="default"'
       --cfg 'feature="rustc-dep-of-std"'
       -C metadata=cd0d33c2bd30ca51
       -C extra-filename=-cd0d33c2bd30ca51
       --out-dir $HOME/riscv/nuttx-rust-app/app/target/riscv32gc-unknown-none-elf/debug/deps
       --target $HOME/riscv/nuttx-rust-app/riscv32gc-unknown-none-elf.json
       -Z force-unstable-if-unmarked
       -L dependency=$HOME/riscv/nuttx-rust-app/app/target/riscv32gc-unknown-none-elf/debug/deps
       -L dependency=$HOME/riscv/nuttx-rust-app/app/target/debug/deps
       --extern core=$HOME/riscv/nuttx-rust-app/app/target/riscv32gc-unknown-none-elf/debug/deps/librustc_std_workspace_core-52e0df2b2cc19b6e.rmeta
       --cap-lints allow
       --cfg 'feature="unstable"'
       --cfg 'feature="mem"'`
   Compiling alloc v0.0.0 ($HOME/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/library/alloc)
     ## Build the Rust Alloc Library for `riscv32gc`
     ## Which will support Heap Memory in future
     Running `$HOME/.rustup/toolchains/nightly-x86_64-apple-darwin/bin/rustc
       --crate-name alloc
       --edition=2021 
       $HOME/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/library/alloc/src/lib.rs
       --error-format=json
       --json=diagnostic-rendered-ansi,artifacts,future-incompat
       --diagnostic-width=94
       --crate-type lib
       --emit=dep-info,metadata,link
       -C embed-bitcode=no
       -C debuginfo=2
       -C metadata=5d7bc2e4f3c29e08
       -C extra-filename=-5d7bc2e4f3c29e08
       --out-dir $HOME/riscv/nuttx-rust-app/app/target/riscv32gc-unknown-none-elf/debug/deps
       --target $HOME/riscv/nuttx-rust-app/riscv32gc-unknown-none-elf.json
       -Z force-unstable-if-unmarked
       -L dependency=$HOME/riscv/nuttx-rust-app/app/target/riscv32gc-unknown-none-elf/debug/deps
       -L dependency=$HOME/riscv/nuttx-rust-app/app/target/debug/deps
       --extern compiler_builtins=$HOME/riscv/nuttx-rust-app/app/target/riscv32gc-unknown-none-elf/debug/deps/libcompiler_builtins-cd0d33c2bd30ca51.rmeta
       --extern core=$HOME/riscv/nuttx-rust-app/app/target/riscv32gc-unknown-none-elf/debug/deps/libcore-d271c6ebb87f9b41.rmeta
       --cap-lints allow`
   Compiling app v0.1.0 ($HOME/riscv/nuttx-rust-app/app)
     ## Compile our Empty Rust Project with Rust Core Library for `riscv32gc`
     ## These are the options that we copied into NuttX Build...
     Running `$HOME/.rustup/toolchains/nightly-x86_64-apple-darwin/bin/rustc
       --crate-name app
       --edition=2021
       src/main.rs
       --error-format=json
       --json=diagnostic-rendered-ansi,artifacts,future-incompat
       --diagnostic-width=94
       --crate-type bin
       --emit=dep-info,link
       -C embed-bitcode=no
       -C debuginfo=2
       -C metadata=1ff442e6481e1397
       -C extra-filename=-1ff442e6481e1397
       --out-dir $HOME/riscv/nuttx-rust-app/app/target/riscv32gc-unknown-none-elf/debug/deps
       --target $HOME/riscv/nuttx-rust-app/riscv32gc-unknown-none-elf.json
       -C incremental=$HOME/riscv/nuttx-rust-app/app/target/riscv32gc-unknown-none-elf/debug/incremental
       -L dependency=$HOME/riscv/nuttx-rust-app/app/target/riscv32gc-unknown-none-elf/debug/deps
       -L dependency=$HOME/riscv/nuttx-rust-app/app/target/debug/deps
       --extern 'noprelude:alloc=$HOME/riscv/nuttx-rust-app/app/target/riscv32gc-unknown-none-elf/debug/deps/liballoc-5d7bc2e4f3c29e08.rlib'
       --extern 'noprelude:compiler_builtins=$HOME/riscv/nuttx-rust-app/app/target/riscv32gc-unknown-none-elf/debug/deps/libcompiler_builtins-cd0d33c2bd30ca51.rlib'
       --extern 'noprelude:core=$HOME/riscv/nuttx-rust-app/app/target/riscv32gc-unknown-none-elf/debug/deps/libcore-d271c6ebb87f9b41.rlib'
       -Z unstable-options`
## Ignore this error. Rust Standard Library and `println` won't work for `riscv32gc`
error[E0463]: can't find crate for `std`
  |
  = note: the `riscv32gc-unknown-none-elf` target may not support the standard library
  = note: `std` is required by `app` because it does not declare `#![no_std]`
  = help: consider building the standard library from source with `cargo build -Zbuild-std`
error: cannot find macro `println` in this scope
 --> src/main.rs:2:5
  |
2 |     println!("Hello, world!");
  |     ^^^^^^^
error: `#[panic_handler]` function required, but not found
For more information about this error, try `rustc --explain E0463`.
error: could not compile `app` (bin "app") due to 3 previous errors
Caused by:
  process didn't exit successfully: `$HOME/.rustup/toolchains/nightly-x86_64-apple-darwin/bin/rustc --crate-name app --edition=2021 src/main.rs --error-format=json --json=diagnostic-rendered-ansi,artifacts,future-incompat --diagnostic-width=94 --crate-type bin --emit=dep-info,link -C embed-bitcode=no -C debuginfo=2 -C metadata=1ff442e6481e1397 -C extra-filename=-1ff442e6481e1397 --out-dir $HOME/riscv/nuttx-rust-app/app/target/riscv32gc-unknown-none-elf/debug/deps --target $HOME/riscv/nuttx-rust-app/riscv32gc-unknown-none-elf.json -C incremental=$HOME/riscv/nuttx-rust-app/app/target/riscv32gc-unknown-none-elf/debug/incremental -L dependency=$HOME/riscv/nuttx-rust-app/app/target/riscv32gc-unknown-none-elf/debug/deps -L dependency=$HOME/riscv/nuttx-rust-app/app/target/debug/deps --extern 'noprelude:alloc=$HOME/riscv/nuttx-rust-app/app/target/riscv32gc-unknown-none-elf/debug/deps/liballoc-5d7bc2e4f3c29e08.rlib' --extern 'noprelude:compiler_builtins=$HOME/riscv/nuttx-rust-app/app/target/riscv32gc-unknown-none-elf/debug/deps/libcompiler_builtins-cd0d33c2bd30ca51.rlib' --extern 'noprelude:core=$HOME/riscv/nuttx-rust-app/app/target/riscv32gc-unknown-none-elf/debug/deps/libcore-d271c6ebb87f9b41.rlib' -Z unstable-options` (exit status: 1)