Rust Custom Target for QEMU RISC-V on Apache NuttX RTOS

📝 21 Apr 2024

Rust Apps on Apache NuttX RTOS and QEMU RISC-V

Thanks to cool-retro-term!

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 modules

Let’s solve the problem! We dive inside the internals of C-to-Rust Interop

Double-Float vs Soft-Float: GCC Linker won’t link the binaries

§1 Software vs Hardware Floating-Point

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 modules

(See the Complete Steps)

GCC 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 \
  ...

(See the GCC Options)

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.o

Which looks like this…

Double-Float vs Soft-Float: GCC Linker won’t link the binaries

Is there a problem?

Watch closely as we compare GCC Compiler with Rust Compiler (pic above)…

GCC CompilerRust 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 CompilerRust Compiler
rv32imafdcriscv32i
- I: Integer- I: Integer
- F: Single Hard-Float(Default is Soft-Float)
- D: Double Hard-Float(Default is Soft-Float)

Double-Float vs Soft-Float: GCC Linker won’t link the binaries

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 ABI

(See the GCC ELF Header)

And 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: 0x0

(See the Rust ELF Header)

Indeed we have a problem: Double-Float and Soft-Float won’t mix! Let’s fix this…

Rust Won’t Double-Float

§2 Rust Won’t Double-Float

What if we ask Rust Compiler to compile for Double-Float? RV32IMAFDC (Pic above)

Let’s harmonise Rust Compiler with GCC Compiler…

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.o

Sorry 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 targets

That’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-elf

But 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 lp64f

We see that GCC supports Double-Float: ilp32d

That’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)

Custom Target for Rust

§3 Custom Target for Rust

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…

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…

In Summary: We spliced Two Built-In Targets into Custom Target riscv32gc

riscv32i
(Built-In)
riscv64gc
(Built-In)
riscv32gc
(Custom)
archriscv32riscv64riscv32
atomic-casfalse
cpugeneric-rv32generic-rv64generic-rv32
data-layoute-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-builtintruetrue
llvm-abinamelp64dilp32d
llvm-targetriscv32riscv64riscv32
max-atomic-width640
target-pointer-width326432

Build the Rust Core Library

§4 Build the Rust Core Library

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…

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
popd

(See the Build Script)

Rust 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.d

Now 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

(See the Build Script)

(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 ABI

(See the ELF Header)

How did we get the Rust Compiler Options?

We copied the above options from cargo build -v

rustc vs cargo build: What’s the diff?

We could have called rustc for building the Rust Core Library. But it will be a bunch of steps with many many options.

NuttX Links OK with Rust

§5 NuttX Links OK with Rust

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
popd

(See the Build Script)

We 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
popd

Our 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`

(See the NuttX Log)

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)

Rust Apps on Apache NuttX RTOS and QEMU RISC-V

§6 Rust Build for 64-bit 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 targets

But Rust Compiler says that riscv64i isn’t a valid Rust Target for 64-bit RISC-V.

Exercise for the Reader:

  1. Is riscv64i the correct target for QEMU?

    (Hint: See this)

    [10 points]


  2. How should we Fix the Build?

    [10 points]


  3. Do we need a Custom Target?

    (Hint: Answer is printed in this article somewhere)

    [10 points]


  4. Will it run on Ox64 BL808 SBC?

    [10 points]


NuttX Links OK with Rust

§7 What’s Next

Today we learnt a bit more about C-to-Rust Interop (pic above)…

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

Build NuttX for QEMU with Rust App

§8 Appendix: Build NuttX for QEMU

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 none

(See the Build Script)

Building our Rust App

§9 Appendix: Rust Compiler Options

How 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-options

The 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
popd
## 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)