lupyuen.org/articles/rust3.html
Lup Yuen Lee 6e2f142414
Some checks are pending
Build Articles / build (push) Waiting to run
Commit from GitHub Actions
2025-01-04 06:09:02 +00:00

795 lines
No EOL
51 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="rustdoc">
<title>Rust Apps on Apache NuttX RTOS and QEMU RISC-V</title>
<!-- Begin scripts/articles/*-header.html: Article Header for Custom Markdown files processed by rustdoc, like chip8.md -->
<meta property="og:title"
content="Rust Apps on Apache NuttX RTOS and QEMU RISC-V"
data-rh="true">
<meta property="og:description"
content="Here's how we run Rust Apps on Apache NuttX RTOS and QEMU RISC-V Emulator"
data-rh="true">
<meta name="description"
content="Here's how we run Rust Apps on Apache NuttX RTOS and QEMU RISC-V Emulator">
<meta property="og:image"
content="https://lupyuen.github.io/images/rust3-title.png">
<meta property="og:type"
content="article" data-rh="true">
<link rel="canonical"
href="https://lupyuen.org/articles/rust3.html" />
<!-- End scripts/articles/*-header.html -->
<!-- Begin scripts/rustdoc-header.html: Header for Custom Markdown files processed by rustdoc, like chip8.md -->
<link rel="alternate" type="application/rss+xml" title="RSS Feed for lupyuen" href="/rss.xml" />
<link rel="stylesheet" type="text/css" href="../normalize.css">
<link rel="stylesheet" type="text/css" href="../rustdoc.css" id="mainThemeStyle">
<link rel="stylesheet" type="text/css" href="../dark.css">
<link rel="stylesheet" type="text/css" href="../light.css" id="themeStyle">
<link rel="stylesheet" type="text/css" href="../prism.css">
<script src="../storage.js"></script><noscript>
<link rel="stylesheet" href="../noscript.css"></noscript>
<link rel="shortcut icon" href="../favicon.ico">
<style type="text/css">
#crate-search {
background-image: url("../down-arrow.svg");
}
</style>
<!-- End scripts/rustdoc-header.html -->
</head>
<body class="rustdoc">
<!--[if lte IE 8]>
<div class="warning">
This old browser is unsupported and will most likely display funky
things.
</div>
<![endif]-->
<!-- Begin scripts/rustdoc-before.html: Pre-HTML for Custom Markdown files processed by rustdoc, like chip8.md -->
<!-- Begin Theme Picker -->
<div class="theme-picker" style="left: 0"><button id="theme-picker" aria-label="Pick another theme!"><img src="../brush.svg"
width="18" alt="Pick another theme!"></button>
<div id="theme-choices"></div>
</div>
<!-- Theme Picker -->
<!-- End scripts/rustdoc-before.html -->
<h1 class="title">Rust Apps on Apache NuttX RTOS and QEMU RISC-V</h1>
<nav id="rustdoc"><ul>
<li><a href="#rust-app-for-nuttx" title="Rust App for NuttX">1 Rust App for NuttX</a><ul></ul></li>
<li><a href="#build-nuttx-for-qemu-risc-v" title="Build NuttX for QEMU RISC-V">2 Build NuttX for QEMU RISC-V</a><ul></ul></li>
<li><a href="#run-nuttx-on-qemu-risc-v" title="Run NuttX on QEMU RISC-V">3 Run NuttX on QEMU RISC-V</a><ul></ul></li>
<li><a href="#console-input-in-rust" title="Console Input in Rust">4 Console Input in Rust</a><ul></ul></li>
<li><a href="#how-nuttx-compiles-rust-apps" title="How NuttX Compiles Rust Apps">5 How NuttX Compiles Rust Apps</a><ul></ul></li>
<li><a href="#software-vs-hardware-floating-point" title="Software vs Hardware Floating-Point">6 Software vs Hardware Floating-Point</a><ul></ul></li>
<li><a href="#panic-is-undefined" title="Panic is Undefined">7 Panic is Undefined</a><ul></ul></li>
<li><a href="#standard-vs-embedded-rust" title="Standard vs Embedded Rust">8 Standard vs Embedded Rust</a><ul></ul></li>
<li><a href="#all-things-considered" title="All Things Considered">9 All Things Considered</a><ul></ul></li>
<li><a href="#whats-next" title="Whats Next">10 Whats Next</a><ul></ul></li>
<li><a href="#appendix-panic-is-undefined" title="Appendix: Panic is Undefined">11 Appendix: Panic is Undefined</a><ul></ul></li>
<li><a href="#appendix-rust-build-for-64-bit-risc-v" title="Appendix: Rust Build for 64-bit RISC-V">12 Appendix: Rust Build for 64-bit RISC-V</a><ul></ul></li></ul></nav><p>📝 <em>7 Apr 2024</em></p>
<p><img src="https://lupyuen.github.io/images/rust3-title.png" alt="Rust Apps on Apache NuttX RTOS and QEMU RISC-V" /></p>
<p>My mentee <a href="https://github.com/apache/nuttx/issues/11907"><strong>Rushabh Gala</strong></a> and I are anxiously awaiting the results of the <a href="https://summerofcode.withgoogle.com/"><strong>Google Summer of Code</strong></a> (GSoC) Project Selection. While waiting, we explain the current steps for running barebones <strong>Rust Apps</strong> on <a href="https://nuttx.apache.org/docs/latest/index.html"><strong>Apache NuttX RTOS</strong></a> (and the challenges we faced)…</p>
<ul>
<li>
<p>How we compile <strong>Rust Apps for NuttX</strong></p>
</li>
<li>
<p>Running NuttX and Rust Apps on <strong>QEMU RISC-V Emulator</strong></p>
</li>
<li>
<p><strong>Console Input and Output</strong> for Rust on NuttX</p>
</li>
<li>
<p><strong>Software vs Hardware Floating-Point</strong> and why its a problem</p>
</li>
<li>
<p>Linking Issues with the <strong>Rust Panic Handler</strong></p>
</li>
<li>
<p><strong>Standard vs Embedded Rust</strong> and why it matters</p>
</li>
<li>
<p>Why were doing all this for <strong>Google Summer of Code</strong></p>
</li>
</ul>
<p>Thanks to <a href="https://pine64.org/"><strong>PINE64</strong></a>, the sponsor of <a href="https://pine64.org/documentation/Ox64/"><strong>Ox64 BL808</strong></a> RISC-V SBCs for our GSoC Project Testing!</p>
<p><img src="https://lupyuen.github.io/images/rust3-output.png" alt="Rust App for NuttX" /></p>
<h1 id="rust-app-for-nuttx"><a class="doc-anchor" href="#rust-app-for-nuttx">§</a>1 Rust App for NuttX</h1>
<p>Below is the <strong>“Hello Rust”</strong> Demo App thats bundled with Apache NuttX RTOS: <a href="https://github.com/apache/nuttx-apps/blob/master/examples/hello_rust/hello_rust_main.rs#L20-L41">hello_rust_main.rs</a></p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment">// main() function not needed
</span><span class="attr">#![no_main]
</span><span class="comment">// Use Rust Core Library (instead of Rust Standard Library)
</span><span class="attr">#![no_std]
</span><span class="comment">// Import printf() from C into Rust
</span><span class="kw">extern </span><span class="string">"C" </span>{
<span class="kw">pub fn </span>printf(
format: <span class="kw-2">*const </span>u8, <span class="comment">// Equivalent to `const char *`
</span>... <span class="comment">// Optional Arguments
</span>) -&gt; i32; <span class="comment">// Returns `int`
</span>} <span class="comment">// TODO: Standardise `i32` as `c_int`</span></code></pre></div>
<p>(Well explain <strong><code>[no_std]</code></strong> in a while)</p>
<p>The code above imports the <em>printf()</em> function from C into Rust.</p>
<p>This is how we call it in Rust: <a href="https://github.com/apache/nuttx-apps/blob/master/examples/hello_rust/hello_rust_main.rs#L54-L74">hello_rust_main.rs</a></p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment">// Main Function exported by Rust to C.
// Don't mangle the Function Name.
</span><span class="attr">#[no_mangle]
</span><span class="kw">pub extern </span><span class="string">"C" </span><span class="kw">fn </span>hello_rust_main(
_argc: i32, <span class="comment">// Equivalent to `int argc`
</span>_argv: <span class="kw-2">*const *const </span>u8 <span class="comment">// Equivalent to `char **argv`
</span>) -&gt; i32 { <span class="comment">// Returns `int`
// Calling a C Function might have Unsafe consequences
</span><span class="kw">unsafe </span>{
printf( <span class="comment">// Call printf() with...
</span><span class="string">b"Hello, Rust!!\n\0" </span><span class="comment">// Byte String terminated by null
</span><span class="kw">as </span><span class="kw-2">*const </span>u8 <span class="comment">// Cast as `const char *`
</span>);
}
<span class="comment">// Exit with status 0
</span><span class="number">0
</span>}</code></pre></div>
<p>Rust expects us to provide a <strong>Panic Handler</strong>. We write a simple one: <a href="https://github.com/apache/nuttx-apps/blob/master/examples/hello_rust/hello_rust_main.rs#L27-L54">hello_rust_main.rs</a></p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment">// Import the Panic Info for our Panic Handler
</span><span class="kw">use </span>core::panic::PanicInfo;
<span class="comment">// Handle a Rust Panic. Needed for [no_std]
</span><span class="attr">#[panic_handler]
</span><span class="kw">fn </span>panic(
_panic: <span class="kw-2">&amp;</span>PanicInfo&lt;<span class="lifetime">'_</span>&gt; <span class="comment">// Receives the Panic Info and Stack Trace
</span>) -&gt; ! { <span class="comment">// Never returns
// TODO: Print the Panic Info and Stack Trace
// For now, we loop forever
</span><span class="kw">loop </span>{}
}</code></pre></div>
<p>Which doesnt do much right now. Well create a proper Panic Handler during GSoC.</p>
<p><img src="https://lupyuen.github.io/images/riscv-build.png" alt="Build Apache NuttX RTOS for 64-bit RISC-V QEMU" /></p>
<h1 id="build-nuttx-for-qemu-risc-v"><a class="doc-anchor" href="#build-nuttx-for-qemu-risc-v">§</a>2 Build NuttX for QEMU RISC-V</h1>
<p><em>How to compile our Rust App?</em></p>
<p>Follow these steps to build Apache NuttX RTOS for <strong>QEMU RISC-V (32-bit)</strong>, bundled with our “Hello Rust” Demo App…</p>
<ol>
<li>
<p>Install the Build Prerequisites, skip the RISC-V Toolchain…</p>
<p><a href="https://lupyuen.github.io/articles/nuttx#install-prerequisites"><strong>“Install Prerequisites”</strong></a></p>
</li>
<li>
<p>Download the RISC-V Toolchain for <strong>riscv64-unknown-elf</strong></p>
<p><a href="https://lupyuen.github.io/articles/riscv#appendix-download-toolchain-for-64-bit-risc-v"><strong>“Download Toolchain for 64-bit RISC-V”</strong></a></p>
</li>
<li>
<p>Download and configure NuttX…</p>
<div class="example-wrap"><pre class="language-bash"><code>mkdir nuttx
cd nuttx
git clone https://github.com/apache/nuttx nuttx
git clone https://github.com/apache/nuttx-apps apps
cd nuttx
tools/configure.sh rv-virt:nsh
make menuconfig</code></pre></div></li>
<li>
<p>In <strong>menuconfig</strong>, browse to “<strong>Device Drivers</strong> &gt; <strong>System Logging</strong></p>
<p>Disable this option…</p>
<div class="example-wrap"><pre class="language-text"><code>Prepend Timestamp to Syslog Message</code></pre></div></li>
<li>
<p>Browse to “<strong>Build Setup</strong> &gt; <strong>Debug Options</strong></p>
<p>Select the following options…</p>
<div class="example-wrap"><pre class="language-text"><code>Enable Debug Features
Enable Error Output
Enable Warnings Output
Enable Informational Debug Output
Enable Debug Assertions
Enable Debug Assertions Show Expression
Scheduler Debug Features
Scheduler Error Output
Scheduler Warnings Output
Scheduler Informational Output</code></pre></div></li>
<li>
<p>Browse to “<strong>Application Configuration</strong> &gt; <strong>Examples</strong></p>
<p>Select “<strong>Hello Rust Example</strong></p>
<p>Select it <strong>Twice</strong> so that “<strong><code>&lt;M&gt;</code></strong>” changes to “<strong><code>&lt;*&gt;</code></strong></p>
<p><a href="https://lupyuen.github.io/articles/rust3#rust-app-for-nuttx">(Source Code for <strong>Hello Rust</strong>)</a></p>
</li>
<li>
<p>Save and exit <strong>menuconfig</strong>.</p>
<p><a href="https://github.com/lupyuen2/wip-pinephone-nuttx/commit/9ee00a20a2f8deab8e27a08cfbc1c7a7f948d5ed">(See the <strong>NuttX Config</strong>)</a></p>
</li>
<li>
<p>Build the NuttX Project and dump the RISC-V Disassembly to <strong>nuttx.S</strong> (for easier troubleshooting)…</p>
<div class="example-wrap"><pre class="language-bash"><code>## Add the Rust Target for RISC-V 32-bit (Soft-Float)
rustup target add riscv32i-unknown-none-elf
## Build the NuttX Project
make
## Dump the NuttX Disassembly to `nuttx.S`
riscv64-unknown-elf-objdump \
-t -S --demangle --line-numbers --wide \
nuttx \
&gt;nuttx.S \
2&gt;&amp;1</code></pre></div>
<p><a href="https://gist.github.com/lupyuen/31c78de72ade71bbdf63372b44749cd4">(See the <strong>Build Log</strong>)</a></p>
<p>This produces the NuttX ELF Image <strong>nuttx</strong> that we may boot on QEMU RISC-V Emulator. (Next Section)</p>
</li>
<li>
<p>If the GCC Linker fails with <em>“Cant link soft-float modules with double-float modules”</em></p>
<div class="example-wrap"><pre class="language-text"><code>$ make
LD: nuttx
riscv64-unknown-elf-ld: nuttx/nuttx/staging/libapps.a
(hello_rust_main.rs...nuttx.apps.examples.hello_rust_1.o):
can&#39;t link soft-float modules with double-float modules
riscv64-unknown-elf-ld: failed to merge target specific data of file
nuttx/staging/libapps.a
(hello_rust_main.rs...nuttx.apps.examples.hello_rust_1.o)</code></pre></div>
<p>Then we patch the <strong>ELF Header</strong> like this, and it should link correctly…</p>
<div class="example-wrap"><pre class="language-bash"><code>xxd -c 1 ../apps/examples/hello_rust/*hello_rust_1.o \
| sed &#39;s/00000024: 00/00000024: 04/&#39; \
| xxd -r -c 1 - /tmp/hello_rust_1.o
cp /tmp/hello_rust_1.o ../apps/examples/hello_rust/*hello_rust_1.o
make</code></pre></div>
<p>(Well come back to this)</p>
</li>
</ol>
<p><img src="https://lupyuen.github.io/images/rust3-title.png" alt="Rust Apps on Apache NuttX RTOS and QEMU RISC-V" /></p>
<h1 id="run-nuttx-on-qemu-risc-v"><a class="doc-anchor" href="#run-nuttx-on-qemu-risc-v">§</a>3 Run NuttX on QEMU RISC-V</h1>
<p>Were ready to <strong>boot NuttX on QEMU Emulator</strong> and run our Rust App!</p>
<ol>
<li>
<p>Download and install <a href="https://www.qemu.org/download/"><strong>QEMU Emulator</strong></a></p>
<div class="example-wrap"><pre class="language-bash"><code>## For macOS:
brew install qemu
## For Debian and Ubuntu:
sudo apt install qemu-system-riscv32</code></pre></div></li>
<li>
<p>Start the <strong>QEMU RISC-V Emulator</strong> (32-bit) with the NuttX ELF Image <strong>nuttx</strong> from the previous section…</p>
<div class="example-wrap"><pre class="language-bash"><code>qemu-system-riscv32 \
-semihosting \
-M virt,aclint=on \
-cpu rv32 \
-bios none \
-kernel nuttx \
-nographic</code></pre></div></li>
<li>
<p>NuttX is now running in the QEMU Emulator! (Pic above)</p>
<div class="example-wrap"><pre class="language-text"><code>NuttShell (NSH) NuttX-12.4.0-RC0
nsh&gt;</code></pre></div></li>
<li>
<p>Enter “<strong>hello_rust</strong>” to run our Rust Demo App (which will print something)</p>
<div class="example-wrap"><pre class="language-text"><code>nsh&gt; hello_rust
Hello, Rust!!</code></pre></div>
<p><a href="https://gist.github.com/lupyuen/31c78de72ade71bbdf63372b44749cd4#file-rust-on-nuttx-build-log-L356-L384">(See the <strong>NuttX Log</strong>)</a></p>
</li>
<li>
<p>Enter “<strong>help</strong>” to see the available commands…</p>
<div class="example-wrap"><pre class="language-text"><code>nsh&gt; help
help usage: help [-v] [&lt;cmd&gt;]
. cp exit mkdir rmdir umount
[ cmp expr mkrd set unset
? dirname false mount sleep uptime
alias dd fdinfo mv source usleep
unalias df free pidof test xd
basename dmesg help printf time
break echo hexdump ps true
cat env kill pwd truncate
cd exec ls rm uname
Builtin Apps:
hello hello_rust nsh ostest sh </code></pre></div></li>
<li>
<p>To Exit QEMU: Press <strong><code>Ctrl-A</code></strong> then <strong><code>x</code></strong></p>
</li>
</ol>
<p><em>What about QEMU for 64-bit RISC-V?</em></p>
<p>Sorry Rust Apps wont build correctly on NuttX for 64-bit RISC-V…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/rust3#appendix-rust-build-for-64-bit-risc-v"><strong>“Rust Build for 64-bit RISC-V”</strong></a></li>
</ul>
<p>Well fix this in GSoC and test it on Ox64 BL808 SBC.</p>
<p><img src="https://lupyuen.github.io/images/rust3-input.png" alt="Console Input in Rust" /></p>
<h1 id="console-input-in-rust"><a class="doc-anchor" href="#console-input-in-rust">§</a>4 Console Input in Rust</h1>
<p><em>Weve done Console Output. How about Console Input?</em></p>
<p>This is how we read <strong>Console Input</strong> in Rust: <a href="https://github.com/lupyuen2/wip-nuttx-apps/blob/rust/examples/hello_rust/hello_rust_main.rs#L20-L49">hello_rust_main.rs</a></p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment">// main() function not needed. Use Rust Core Library.
</span><span class="attr">#![no_main]
#![no_std]
</span><span class="comment">// Import the Types for C Interop
</span><span class="kw">use </span>core::ffi::{ c_char, c_int, c_void };
<span class="comment">// Import the Functions from C into Rust
</span><span class="kw">extern </span><span class="string">"C" </span>{
<span class="kw">pub fn </span>printf(format: <span class="kw-2">*const </span>u8, ...) -&gt; i32;
<span class="kw">pub fn </span>puts(s: <span class="kw-2">*const </span>c_char) -&gt; c_int;
<span class="kw">pub fn </span>fgets(buf: <span class="kw-2">*mut </span>c_char, n: c_int, stream: <span class="kw-2">*mut </span>c_void) -&gt; <span class="kw-2">*mut </span>c_char;
<span class="kw">pub fn </span>lib_get_stream(fd: c_int) -&gt; <span class="kw-2">*mut </span>c_void;
}</code></pre></div>
<p>The code above imports the <em>fgets()</em> function from C into Rust.</p>
<p>Calling <em>fgets()</em> is a little more complicated: <a href="https://github.com/lupyuen2/wip-nuttx-apps/blob/rust/examples/hello_rust/hello_rust_main.rs#L66-L95">hello_rust_main.rs</a></p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment">// Main Function exported by Rust to C
</span><span class="attr">#[no_mangle]
</span><span class="kw">pub extern </span><span class="string">"C" </span><span class="kw">fn </span>hello_rust_main(_argc: i32, _argv: <span class="kw-2">*const *const </span>u8) -&gt; i32 {
<span class="comment">// Receive some text from Standard Input and print it
</span><span class="kw">unsafe </span>{
<span class="comment">// Standard Input comes from https://github.com/apache/nuttx/blob/master/include/stdio.h#L64-L68
</span><span class="kw">let </span>stdin: <span class="kw-2">*mut </span>c_void = <span class="comment">// Equivalent to `void *`
</span>lib_get_stream(<span class="number">0</span>); <span class="comment">// Init to Stream 0 (stdin)
// Input Buffer with 256 chars (including terminating null)
</span><span class="kw">let </span><span class="kw-2">mut </span>buf: [c_char; <span class="number">256</span>] = <span class="comment">// Input Buffer is Mutable (will change)
</span>[<span class="number">0</span>; <span class="number">256</span>]; <span class="comment">// Init with nulls
// Read a line from Standard Input
</span><span class="kw">if </span>!fgets(
<span class="kw-2">&amp;mut </span>buf[<span class="number">0</span>], <span class="comment">// Input Buffer
</span>buf.len() <span class="kw">as </span>i32, <span class="comment">// Buffer Size
</span>stdin <span class="comment">// Standard Input
</span>).is_null() { <span class="comment">// Catch the Input Error
// Print the line
</span>printf(<span class="string">b"You entered...\n\0" </span><span class="kw">as </span><span class="kw-2">*const </span>u8);
puts(<span class="kw-2">&amp;</span>buf[<span class="number">0</span>]);
}
}
<span class="comment">// Exit with status 0
</span><span class="number">0
</span>}
<span class="comment">// Omitted: Panic Handler</span></code></pre></div>
<p>This gets a bit dangerous… The <strong>Input Buffer might Overflow</strong> if were not careful with the Parameters!</p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment">// Read a line from Standard Input
</span>fgets(
<span class="kw-2">&amp;mut </span>buf[<span class="number">0</span>], <span class="comment">// Input Buffer
</span>buf.len() <span class="kw">as </span>i32, <span class="comment">// Buffer Size
</span>stdin <span class="comment">// Standard Input
</span>);</code></pre></div>
<p>Which makes us ponder about <a href="https://en.m.wikipedia.org/wiki/Memory_safety"><strong>Memory Safety</strong></a>: <em>“Hmmm the fgets() buffer size… Does it include the terminating null?”</em> <a href="https://man.archlinux.org/man/fgets.3p.en">(Yep it does!)</a></p>
<p><em>What about Rust? Does it safely handle Console Input?</em></p>
<p>Reading the <a href="https://doc.rust-lang.org/std/io/fn.stdin.html"><strong>Standard Input in Rust</strong></a> looks simpler and safer…</p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment">// Allocate an Input Buffer from Heap Memory
</span><span class="kw">let </span><span class="kw-2">mut </span>buffer = String::new();
<span class="comment">// Read a line from Standard Input
</span>io::stdin().read_line(<span class="kw-2">&amp;mut </span>buffer)<span class="question-mark">?</span>;</code></pre></div>
<p>But this wont work on NuttX because…</p>
<ul>
<li>
<p><strong>Rust Standard Input</strong> <em>io::stdin()</em> isnt supported on Embedded Platforms</p>
</li>
<li>
<p><strong>Dynamic Strings and Heap Memory</strong> wont work on Embedded Platforms either</p>
</li>
</ul>
<p><em>Bummer. How to do I/O safely on NuttX?</em></p>
<p>During GSoC we shall…</p>
<ul>
<li>
<p>Create <strong>Rust Wrappers</strong> that will safely call NuttX POSIX Functions: <em>open(), close(), ioctl(), …</em></p>
</li>
<li>
<p>Which will support <a href="https://rust-for-linux.github.io/docs/v6.8-rc3/kernel/str/struct.CString.html"><strong>Simple Strings</strong></a> via <em>malloc()</em></p>
</li>
</ul>
<p><a href="https://github.com/bytecodealliance/rustix/tree/nuttx"><strong>Rustix Project</strong></a> tried to provide <a href="https://www.youtube.com/watch?v=JTJW6kOqf9I"><strong>Comprehensive Rust Wrappers</strong></a> for NuttX POSIX. Sadly the <a href="https://github.com/bytecodealliance/rustix/commits/nuttx/"><strong>project has stalled</strong></a>. Well implement simpler, lighter wrappers instead.</p>
<p><img src="https://lupyuen.github.io/images/rust3-build.png" alt="How NuttX Compiles Rust Apps" /></p>
<h1 id="how-nuttx-compiles-rust-apps"><a class="doc-anchor" href="#how-nuttx-compiles-rust-apps">§</a>5 How NuttX Compiles Rust Apps</h1>
<p><em>What happens when we compile our Rust App?</em></p>
<p>Watch how NuttX builds Rust Apps by calling <a href="https://doc.rust-lang.org/rustc/what-is-rustc.html"><strong><code>rustc</code></strong></a>. (Instead of the usual <a href="https://doc.rust-lang.org/cargo/commands/cargo-build.html"><strong><code>cargo</code> <code>build</code></strong></a>)</p>
<div class="example-wrap"><pre class="language-bash"><code>## Build the NuttX Project with Tracing Enabled
$ make --trace
## Compile `hello_rust_main.rs` to `hello_rust.o`
## for Rust Target: RISC-V 32-bit (Soft-Float)
rustc \
--edition 2021 \
--emit obj \
-g \
--target riscv32i-unknown-none-elf \
-C panic=abort \
-O \
hello_rust_main.rs \
-o hello_rust_main.rs...apps.examples.hello_rust.o
## Copy `hello_rust.o` to `hello_rust_1.o` (Why?)
cp \
hello_rust_main.rs...apps.examples.hello_rust.o \
hello_rust_main.rs...apps.examples.hello_rust_1.o
## Omitted: Bundle `hello_rust_1.o`
## into library `staging/libapps.a`
## Link `staging/libapps.a` into `nuttx`
riscv64-unknown-elf-ld \
--entry=__start \
-melf32lriscv \
--gc-sections \
-nostdlib \
--cref \
-Map=nuttx/nuttx.map \
-Tboards/risc-v/qemu-rv/rv-virt/scripts/ld.script.tmp \
-L staging \
-L arch/risc-v/src/board \
-o nuttx \
qemu_rv_head.o \
--start-group \
-lsched \
-ldrivers \
-lboards \
-lc \
-lmm \
-larch \
-lm \
-lapps \
-lfs \
-lbinfmt \
-lboard riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-apple-darwin/bin/../lib/gcc/riscv64-unknown-elf/10.2.0/rv32imafdc/ilp32d/libgcc.a \
--end-group</code></pre></div>
<p><a href="https://gist.github.com/lupyuen/1d79670339480baed19f4fa30266b945">(See the <strong>Detailed Build Log</strong>)</a></p>
<p><a href="https://github.com/apache/nuttx-apps/blob/master/Application.mk#L164-L170">(<strong>Rust Build</strong> with <strong><code>rustc</code></strong> is defined here)</a></p>
<p><a href="https://github.com/apache/nuttx/pull/5566">(Why NuttX calls <strong><code>rustc</code></strong> instead of <strong><code>cargo</code> <code>build</code></strong>)</a></p>
<p>Here are the <strong>Rust Binaries</strong> produced by the NuttX Build (which will be linked into the NuttX Firmware)…</p>
<div class="example-wrap"><pre class="language-text"><code>$ ls -l ../apps/examples/hello_rust
total 112
-rw-r--r-- 1 650 Jul 20 2023 Kconfig
-rw-r--r-- 1 1071 Jul 20 2023 Make.defs
-rw-r--r-- 1 141 Mar 17 09:44 Make.dep
-rw-r--r-- 1 1492 Mar 16 20:41 Makefile
-rw-r--r-- 1 3982 Mar 17 00:06 hello_rust_main.rs
-rw-r--r-- 1 13168 Mar 17 09:44 hello_rust_main.rs...apps.examples.hello_rust.o
-rw-r--r-- 1 18240 Mar 17 09:54 hello_rust_main.rs...apps.examples.hello_rust_1.o</code></pre></div>
<p><a href="https://gist.github.com/lupyuen/76b8680a58793571db67082bcca2e86c">(See the <strong>RISC-V Disassembly</strong>)</a></p>
<p>Now that we understand the build, lets talk about the hiccups…</p>
<p><img src="https://lupyuen.github.io/images/rust3-float.png" alt="Cant link soft-float modules with double-float modules" /></p>
<h1 id="software-vs-hardware-floating-point"><a class="doc-anchor" href="#software-vs-hardware-floating-point">§</a>6 Software vs Hardware Floating-Point</h1>
<p><em>Whats this error? “Cant link soft-float modules with double-float modules”</em></p>
<div class="example-wrap"><pre class="language-bash"><code>$ make
LD: nuttx
riscv64-unknown-elf-ld: nuttx/nuttx/staging/libapps.a
(hello_rust_main.rs...nuttx.apps.examples.hello_rust_1.o):
can&#39;t link soft-float modules with double-float modules
riscv64-unknown-elf-ld: failed to merge target specific data of file
nuttx/staging/libapps.a
(hello_rust_main.rs...nuttx.apps.examples.hello_rust_1.o)</code></pre></div>
<p>GCC Linker failed to link the <strong>Compiled Rust Binary</strong> <em>(hello_rust_1.o)</em> into our NuttX Firmware because…</p>
<ul>
<li>
<p>Rust Binary <em>hello_rust_1.o</em> was compiled with…</p>
<p><strong>Software Floating-Point</strong> <em>(“soft-float”)</em></p>
</li>
<li>
<p>But NuttX Firmware was compiled with…</p>
<p><strong>Double Precision Hardware Floating-Point</strong> <em>(“double-float”)</em></p>
</li>
</ul>
<p>The two are incompatible. And the GCC Linking fails.</p>
<p><em>How to fix this?</em></p>
<p>For now we <strong>Patch the ELF Header</strong> of our Rust Object File. And NuttX Firmware will link correctly…</p>
<div class="example-wrap"><pre class="language-bash"><code>## Patch ELF Header from Soft-Float to Double-Float
xxd -c 1 ../apps/examples/hello_rust/*hello_rust_1.o \
| sed &#39;s/00000024: 00/00000024: 04/&#39; \
| xxd -r -c 1 - /tmp/hello_rust_1.o
cp /tmp/hello_rust_1.o ../apps/examples/hello_rust/*hello_rust_1.o
make
## NuttX links OK. Ignore these warnings: (why?)
## riscv64-unknown-elf-ld: warning: nuttx/staging/libapps.a(hello_rust_main.rs...nuttx.apps.examples.hello_rust_1.o):
## mis-matched ISA version 2.1 for &#39;i&#39; extension, the output version is 2.0</code></pre></div>
<p><em>What exactly are we patching in the ELF Header?</em></p>
<p>Inside the <a href="https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header"><strong>ELF Header</strong></a> of an Object File: Theres a Flag (at Offset <strong><code>0x24</code></strong>) that says whether it was compiled for…</p>
<ul>
<li>
<p><strong>Software Floating-Point:</strong> Flags = 0, or…</p>
</li>
<li>
<p><strong>Double-Precision Hardware Floating-Point:</strong> Flags = 4</p>
</li>
</ul>
<p>We <strong>modified the Flag</strong> in the ELF Header so that it says <strong>Double-Float</strong></p>
<div class="example-wrap"><pre class="language-bash"><code>## Before Patching: ELF Header says Software Floating-Point
$ riscv64-unknown-elf-readelf -h -A ../apps/examples/hello_rust/*hello_rust_1.o
Flags: 0x0
## After Patching: ELF Header says Double-Precision Hardware Floating-Point
$ riscv64-unknown-elf-readelf -h -A ../apps/examples/hello_rust/*hello_rust_1.o
Flags: 0x4, double-float ABI</code></pre></div>
<p>And it links correctly!</p>
<p><a href="https://lupyuen.github.io/articles/zig#patch-elf-header">(We had a similar issue with <strong>Zig Compiler</strong>)</a></p>
<p><em>But why Soft-Float instead of Double-Float? (Mmmm ice cream float)</em></p>
<p>Yeah patching the ELF Header is a Bad Hack! Heres the complete analysis and proper solution…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/rust4"><strong>“Rust Custom Target for QEMU RISC-V on Apache NuttX RTOS”</strong></a></li>
</ul>
<p><img src="https://lupyuen.github.io/images/rust3-panic.png" alt="Undefined reference to core::panicking::panic" /></p>
<h1 id="panic-is-undefined"><a class="doc-anchor" href="#panic-is-undefined">§</a>7 Panic is Undefined</h1>
<p><em>Whats this core::panicking::panic? Why is it undefined?</em></p>
<div class="example-wrap"><pre class="language-bash"><code>$ make
riscv64-unknown-elf-ld:
nuttx/staging/libapps.a(hello_rust_main.rs...apps.examples.hello_rust_1.o):
in function `no symbol&#39;:
apps/examples/hello_rust/hello_rust_main.rs:90:
undefined reference to `core::panicking::panic&#39;</code></pre></div>
<p>Suppose were reading <strong>Console Input</strong> in our Rust App: <a href="https://github.com/lupyuen2/wip-nuttx-apps/blob/rust2/examples/hello_rust/hello_rust_main.rs#L81-L86">hello_rust_main.rs</a></p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment">// Input Buffer with 256 chars (including terminating null)
</span><span class="kw">let </span><span class="kw-2">mut </span>buf: [c_char; <span class="number">256</span>] = <span class="comment">// Input Buffer is Mutable (will change)
</span>[<span class="number">0</span>; <span class="number">256</span>]; <span class="comment">// Init with nulls
// Read a line from Standard Input
</span>fgets(
<span class="kw-2">&amp;mut </span>buf[<span class="number">0</span>], <span class="comment">// Buffer
</span>buf.len() <span class="kw">as </span>i32 - <span class="number">1</span>, <span class="comment">// Size (cast to Signed Integer)
</span>stdin <span class="comment">// Standard Input
</span>);</code></pre></div>
<p><em>“buf.len() - 1”</em> might <strong>Panic and Halt</strong>. <a href="https://gist.github.com/lupyuen/905c8f47155376c888efb35544afeaf7#file-panic-undefined-s-L279-L288">(Why?)</a></p>
<p>To implement the panic, Rust Compiler inserts a call to the Core Function <em>core::panicking::panic</em>.</p>
<p>(Which comes from the <a href="https://doc.rust-lang.org/core/index.html"><strong>Rust Core Library</strong></a>)</p>
<p><em>And the Panic Function is missing somehow?</em></p>
<p>Rushabh has implemented a fix for the Undefined Panic Function…</p>
<ul>
<li><a href="https://github.com/apache/nuttx-apps/pull/2333"><strong>Add <code>-O</code> to <code>RUSTFLAGS</code> in Makefile</strong></a></li>
</ul>
<p>But when we add <strong>Another Point of Panic</strong>: We see the Undefined Panic Error again (sigh)…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/rust3#appendix-panic-is-undefined"><strong>“Panic is Undefined”</strong></a></li>
</ul>
<p><em>Whats causing this Undefined Panic Function?</em></p>
<p>According to <a href="https://github.com/rust-lang/compiler-builtins/issues/79"><strong>this discussion</strong></a>, the Rust Core Library is compiled with <a href="https://nnethercote.github.io/perf-book/build-configuration.html#link-time-optimization"><strong>Link-Time Optimisation (LTO)</strong></a>. (Including the Panic Function)</p>
<p>But were linking it into our NuttX Firmware with GCC Linker, with <a href="https://johnysswlab.com/link-time-optimizations-new-way-to-do-compiler-optimizations/"><strong>LTO Disabled</strong></a> (by default). Which causes the Missing Panic Function.</p>
<p><em>How is this different from typical Rust Builds?</em></p>
<p>Normally we run <a href="https://doc.rust-lang.org/cargo/commands/cargo-build.html"><strong><code>cargo</code> <code>build</code></strong></a> to compile our Embedded Rust Apps. And it handles LTO correctly.</p>
<p>But NuttX calls <a href="https://doc.rust-lang.org/rustc/what-is-rustc.html"><strong><code>rustc</code></strong></a> to compile Rust Apps, then calls <strong>GCC Linker</strong> to link into our NuttX Firmware. Which doesnt seem to support LTO.</p>
<p>Well sort this out in GSoC!</p>
<p><a href="https://github.com/apache/nuttx/pull/5566">(Why NuttX calls <strong><code>rustc</code></strong> instead of <strong><code>cargo</code> <code>build</code></strong>)</a></p>
<p>(Which means we cant import Rust Crates from <a href="https://crates.io/"><strong><code>crates.io</code></strong></a>!)</p>
<blockquote>
<p><img src="https://lupyuen.github.io/images/rust3-nostd.jpg" alt="The Embedded Rust Book" /></p>
</blockquote>
<blockquote>
<p><a href="https://docs.rust-embedded.org/book/intro/no-std.html"><em>The Embedded Rust Book</em></a></p>
</blockquote>
<h1 id="standard-vs-embedded-rust"><a class="doc-anchor" href="#standard-vs-embedded-rust">§</a>8 Standard vs Embedded Rust</h1>
<p><em>What is [no_std]? Will Rust call C Standard Library, like for malloc()?</em></p>
<p>Earlier we saw <strong><code>[no_std]</code></strong> inside our <a href="https://lupyuen.github.io/articles/rust3#rust-app-for-nuttx"><strong>Rust App</strong></a>.</p>
<p>There are 2 “flavours” of Rust, depending on the Rust Libraries that we use:</p>
<ul>
<li>
<p><a href="https://doc.rust-lang.org/std/"><strong>Rust Standard Library</strong></a>: This is used by most Rust Apps on Desktops and Servers.</p>
<p>Supports Heap Memory and the Rust Equivalent of POSIX Calls.</p>
</li>
<li>
<p><a href="https://doc.rust-lang.org/core/index.html"><strong>Rust Core Library</strong></a> <code>[no_std]</code>: Barebones Rust Library that runs on Bare Metal, used by Rust Embedded Apps.</p>
<p>Calls <a href="https://gist.github.com/lupyuen/ac2b43f2e31ecf0d972dcf5fed8d5e4c#file-hello_rust_1-s-L187"><strong>minimal functions</strong></a> in C Standard Library. Doesnt support Heap Memory and POSIX.</p>
</li>
</ul>
<p>The <em>malloc()</em> that we mentioned: Its called by the <strong>Rust Standard Library</strong>. <a href="https://github.com/rust-lang/rust/blob/c8813ddd6d2602ae5473752031fd16ba70a6e4a7/library/std/src/sys/pal/unix/alloc.rs#L14">(Like this)</a></p>
<p><em>What about Rust Drivers for NuttX Kernel?</em></p>
<p><strong>For Kernel Dev</strong> <a href="https://rust-for-linux.com/third-party-crates#introduction:~:text=Some%20of%20those%20open%2Dsource%20libraries%20are%20potentially%20usable%20in%20the%20kernel%20because%20they%20only%20depend%20on%20core%20and%20alloc%20(rather%20than%20std)%2C%20or%20because%20they%20only%20provide%20macro%20facilities.">(like <strong>Linux</strong>)</a>: Well use the <strong>Rust Core Library</strong>. Which doesnt support Heap Memory and doesnt need <em>malloc()</em>.</p>
<p><em>But most Kernel Drivers will need Kernel Heap!</em></p>
<p>Thats why Linux Kernel supports the <a href="https://doc.rust-lang.org/alloc/#"><strong><code>alloc</code> Rust Library / Crate</strong></a> for Heap Memory. To implement Rust <strong><code>alloc</code></strong>, Linux Kernel calls <em>krealloc()</em> to allocate Kernel Heap. <a href="https://github.com/torvalds/linux/blob/741e9d668aa50c91e4f681511ce0e408d55dd7ce/rust/kernel/allocator.rs#L46">(Like this)</a></p>
<p><strong>For NuttX Kernel:</strong> Well implement Rust <strong><code>alloc</code></strong> by calling <em>kmm_malloc()</em>.</p>
<p><em>Anything else we need for Rust in NuttX Kernel?</em></p>
<p>Since were calling <strong>Rust Core Library</strong> in NuttX Kernel, we wont touch any POSIX Application Interfaces. Thus if we need to support the Kernel Equivalent of Errno (and other Global State), well have to <strong>create the Rust Library</strong> ourselves.</p>
<p><a href="https://rust-for-linux.github.io/docs/v6.8-rc3/kernel/">(See the Rust Library for <strong>Linux Kernel</strong>)</a></p>
<p>(GSoC Project Report will discuss a <strong>Simple LED Driver</strong> in Rust for NuttX Kernel)</p>
<p><img src="https://lupyuen.github.io/images/rust3-ideas.jpg" alt="GSoC 2024 Ideas" /></p>
<h1 id="all-things-considered"><a class="doc-anchor" href="#all-things-considered">§</a>9 All Things Considered</h1>
<ol>
<li>
<p><em>Why are we doing all this?</em></p>
<p>Yeah its tough work but it needs to be done because…</p>
<p>— Some folks are urging us to explore <a href="https://www.whitehouse.gov/oncd/briefing-room/2024/02/26/press-release-technical-report/"><strong>Memory-Safe Programming in Rust</strong></a></p>
<p>— NuttX Devs among us might already be coding <strong>Rust Apps and Rust Drivers</strong> for NuttX? (We know of one Corporate User of NuttX thats super keen on Rust)</p>
<p>— Hence were helpfully drafting the <a href="https://github.com/apache/nuttx/issues/11907"><strong>Standards and Guidelines</strong></a> for folks already coding Rust in NuttX</p>
</li>
<li>
<p><em>Learning Rust looks kinda hard. Any other way to write Memory-Safe Apps?</em></p>
<p>If were familiar with Python: Check out the <a href="https://lupyuen.github.io/articles/nim"><strong>Nim Programming Language</strong></a>.</p>
<p><a href="https://lupyuen.github.io/articles/sensor"><strong>Zig Programming Language</strong></a> is safer than C and easier to learn. But not quite Memory-Safe like Rust.</p>
<p><a href="https://gist.github.com/lupyuen/10ce1aeff7f6a743c374aa7c1931525b"><strong>AI Tools</strong></a> might be helpful for coding the difficult bits of Rust: ChatGPT, GitHub Copilot, Google Gemini, …</p>
<p>(Well validate this during GSoC)</p>
</li>
<li>
<p><em>Giving in to our AI Overlords already?</em></p>
<p>But Rust Devs are familiar with smarty tools. <a href="https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html#the-borrow-checker"><strong>Borrow Checker</strong></a> and <a href="https://doc.rust-lang.org/clippy/index.html"><strong>Cargo Clippy</strong></a> are already so clever, they might as well be AI!</p>
<p>And Rust Compiler is almost Sentient, always commanding us Humans: <em>“Please do this to fix the build, you poopy nincompoop!”</em></p>
<p>(My Biggest Wish: Someone please create a <strong>Higher-Level Dialect</strong> of Rust that will use bits of AI to compile into the present Low-Level Rust. Which might simplify Generics, Lifetimes, Box, Rc, Arc, RefCell, Fn*, dyn, async, …)</p>
</li>
<li>
<p><em>Will there be Resistance to Rust Drivers inside NuttX Kernel?</em></p>
<p>Ouch were trapped between a Rock and… Another Rusty Rock!</p>
<p><strong>NuttX Devs</strong> are concerned about the <a href="https://lists.apache.org/thread/q09w8p6pm683rvzvrwdwv4cf0bbqmfg2"><strong>extra complexity</strong></a> that Rust Drivers add to the Kernel Build</p>
<p><strong>Rust Community</strong> is probably thinking were <strong>not doing enough</strong> to promote Memory-Safe Coding in NuttX Kernel</p>
<p>For now we walk the <strong>Middle Way</strong></p>
<p><strong>Lay the Groundwork</strong> for Future Integration of Rust Drivers into NuttX Kernel</p>
<p>— Observe the Rust Development in <a href="https://rust-for-linux.com/"><strong>Linux Kernel</strong></a> and <a href="https://github.com/zephyrproject-rtos/zephyr/issues/65837"><strong>Zephyr OS</strong></a>. Then adapt the Best Practices for NuttX Kernel.</p>
</li>
</ol>
<p><img src="https://lupyuen.github.io/images/rust3-gsoc.png" alt="GSoC Proposals for NuttX" /></p>
<h1 id="whats-next"><a class="doc-anchor" href="#whats-next">§</a>10 Whats Next</h1>
<p>Coming This Summer: Plenty to be done for <strong>Rust Apps on Apache NuttX RTOS</strong>!</p>
<ul>
<li>
<p>Compiling <strong>Rust Apps for NuttX</strong> works mostly OK, but could be improved</p>
</li>
<li>
<p>Rust Apps run OK on <strong>QEMU 32-bit RISC-V Emulator</strong>, though somewhat broken on 64-bit RISC-V (like Ox64 BL808 SBC)</p>
</li>
<li>
<p><strong>Software vs Hardware Floating-Point</strong> becomes a problem for 32-bit and 64-bit RISC-V Platforms, we should remove the awful patchy hack</p>
</li>
<li>
<p><strong>Link-Time Optimisation</strong> causes Linking Issues with the Rust Panic Handler</p>
</li>
<li>
<p><strong>Console Input / Output</strong> works fine for Rust on NuttX. Well explore safer ways to call NuttX POSIX Functions.</p>
</li>
<li>
<p>All this will happen during <strong>Google Summer of Code</strong>!</p>
</li>
</ul>
<p>Check out the next article…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/rust4"><strong>“Rust Custom Target for QEMU RISC-V on Apache NuttX RTOS”</strong></a></li>
</ul>
<p>Many Thanks to my <a href="https://lupyuen.github.io/articles/sponsor"><strong>GitHub Sponsors</strong></a> (and the awesome NuttX Community) for supporting my work! This article wouldnt have been possible without your support.</p>
<ul>
<li>
<p><a href="https://lupyuen.github.io/articles/sponsor"><strong>Sponsor me a coffee</strong></a></p>
</li>
<li>
<p><a href="https://news.ycombinator.com/item?id=39956407"><strong>Discuss this article on Hacker News</strong></a></p>
</li>
<li>
<p><a href="https://github.com/lupyuen/nuttx-ox64"><strong>My Current Project: “Apache NuttX RTOS for Ox64 BL808”</strong></a></p>
</li>
<li>
<p><a href="https://github.com/lupyuen/nuttx-star64"><strong>My Other Project: “NuttX for Star64 JH7110”</strong></a></p>
</li>
<li>
<p><a href="https://github.com/lupyuen/pinephone-nuttx"><strong>Older Project: “NuttX for PinePhone”</strong></a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io"><strong>Check out my articles</strong></a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io/rss.xml"><strong>RSS Feed</strong></a></p>
</li>
</ul>
<p><em>Got a question, comment or suggestion? Create an Issue or submit a Pull Request here…</em></p>
<p><a href="https://github.com/lupyuen/lupyuen.github.io/blob/master/src/rust3.md"><strong>lupyuen.github.io/src/rust3.md</strong></a></p>
<p><img src="https://lupyuen.github.io/images/rust3-panic.png" alt="Undefined reference to core::panicking::panic" /></p>
<h1 id="appendix-panic-is-undefined"><a class="doc-anchor" href="#appendix-panic-is-undefined">§</a>11 Appendix: Panic is Undefined</h1>
<p><em>Whats this core::panicking::panic? Why is it undefined?</em></p>
<div class="example-wrap"><pre class="language-bash"><code>$ make
riscv64-unknown-elf-ld:
nuttx/staging/libapps.a(hello_rust_main.rs...apps.examples.hello_rust_1.o):
in function `no symbol&#39;:
apps/examples/hello_rust/hello_rust_main.rs:90:
undefined reference to `core::panicking::panic&#39;</code></pre></div>
<p>Earlier we spoke about the Undefined Panic Function…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/rust3#panic-is-undefined"><strong>“Panic is Undefined”</strong></a></li>
</ul>
<p>Which Rushabh has fixed with this patch…</p>
<ul>
<li><a href="https://github.com/apache/nuttx-apps/pull/2333"><strong>Add <code>-O</code> to <code>RUSTFLAGS</code> in Makefile</strong></a></li>
</ul>
<p>But watch what happens when we add <strong>Another Point of Panic</strong></p>
<p>Below is our Test Code that has <strong>Two Potential Panics</strong>: <a href="https://github.com/lupyuen2/wip-nuttx-apps/blob/rust2/examples/hello_rust/hello_rust_main.rs#L70-L103">hello_rust_main.rs</a></p>
<ol>
<li>
<p><a href="https://github.com/lupyuen2/wip-pinephone-nuttx-apps/blob/rust2/examples/hello_rust/hello_rust_main.rs#L84"><strong>Buffer Length</strong></a> might panic. <a href="https://gist.github.com/lupyuen/ac2b43f2e31ecf0d972dcf5fed8d5e4c#file-hello_rust_1-s-L301-L310">(Why?)</a></p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment">// Input Buffer with 256 chars (including terminating null)
</span><span class="kw">let </span><span class="kw-2">mut </span>buf: [c_char; <span class="number">256</span>] = <span class="comment">// Input Buffer is Mutable (will change)
</span>[<span class="number">0</span>; <span class="number">256</span>]; <span class="comment">// Init with nulls
// Read a line from Standard Input
</span>fgets(
<span class="kw-2">&amp;mut </span>buf[<span class="number">0</span>], <span class="comment">// Buffer
// This might Panic! (Why?)
</span>buf.len() <span class="kw">as </span>i32 - <span class="number">1</span>, <span class="comment">// Unsigned Size cast to Signed Integer
</span>stdin <span class="comment">// Standard Input
</span>);</code></pre></div>
</li>
<li>
<p><a href="https://github.com/lupyuen2/wip-pinephone-nuttx-apps/blob/rust2/examples/hello_rust/hello_rust_main.rs#L90"><strong>Divide by Zero</strong></a> will also panic</p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment">// Buffer might begin with null
// Which causes Divide by Zero
</span><span class="kw">let </span>i = <span class="number">1 </span>/ buf[<span class="number">0</span>];</code></pre></div>
</li>
</ol>
<p><em>What happens when we compile this?</em></p>
<p><strong>If we omit <code>RUSTFLAGS=-O</code></strong>: We see Two Undefined Panic Functions…</p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code>apps/examples/hello_rust/hello_rust_main.rs:<span class="number">84
</span>buf.len() <span class="kw">as </span>i32 - <span class="number">1</span>, <span class="comment">// Might Panic (Why?)
</span>a0: <span class="number">00000097 </span>auipc ra,<span class="number">0x0
</span>a0: R_RISCV_CALL_PLT core::panicking::panic
apps/examples/hello_rust/hello_rust_main.rs:<span class="number">90
</span><span class="kw">let </span>i = <span class="number">1 </span>/ buf[<span class="number">0</span>]; <span class="comment">// Might Divide by Zero
</span><span class="number">108</span>: <span class="number">00000097 </span>auipc ra,<span class="number">0x0
108</span>: R_RISCV_CALL_PLT core::panicking::panic</code></pre></div>
<p><a href="https://gist.github.com/lupyuen/ac2b43f2e31ecf0d972dcf5fed8d5e4c#file-hello_rust_1-s-L301-L352">(See the <strong>RISC-V Disassembly</strong>)</a></p>
<p><strong>After we add <code>RUSTFLAGS=-O</code></strong>: We still see One Undefined Panic Function for the divide-by-zero…</p>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code>apps/examples/hello_rust/hello_rust_main.rs:<span class="number">90
</span><span class="kw">let </span>i = <span class="number">1 </span>/ buf[<span class="number">0</span>]; <span class="comment">// Might Divide by Zero
</span>d0: <span class="number">00000097 </span>auipc ra,<span class="number">0x0
</span>d0: R_RISCV_CALL_PLT core::panicking::panic</code></pre></div>
<p><a href="https://gist.github.com/lupyuen/bec3bdd8379143a6046414d3ad2cc888#file-hello_rust_1-s-L287-L294">(See the <strong>RISC-V Disassembly</strong>)</a></p>
<p>Which leads to the Undefined Panic Error again (sigh)…</p>
<div class="example-wrap"><pre class="language-bash"><code>$ make
riscv64-unknown-elf-ld:
nuttx/staging/libapps.a(hello_rust_main.rs...apps.examples.hello_rust_1.o):
in function `no symbol&#39;:
apps/examples/hello_rust/hello_rust_main.rs:90:
undefined reference to `core::panicking::panic&#39;</code></pre></div>
<p><em>Whats causing this Undefined Panic Function?</em></p>
<p>According to <a href="https://github.com/rust-lang/compiler-builtins/issues/79"><strong>this discussion</strong></a>, the Rust Core Library is compiled with <a href="https://nnethercote.github.io/perf-book/build-configuration.html#link-time-optimization"><strong>Link-Time Optimisation (LTO)</strong></a>. (Including the Panic Function)</p>
<p>But were linking it into our NuttX Firmware with GCC Linker, with <a href="https://johnysswlab.com/link-time-optimizations-new-way-to-do-compiler-optimizations/"><strong>LTO Disabled</strong></a> (by default). Which causes the Missing Panic Function.</p>
<p><em>How is this different from typical Rust Builds?</em></p>
<p>Normally we run <a href="https://doc.rust-lang.org/cargo/commands/cargo-build.html"><strong><code>cargo</code> <code>build</code></strong></a> to compile our Embedded Rust Apps. And it handles LTO correctly.</p>
<p>But NuttX calls <a href="https://doc.rust-lang.org/rustc/what-is-rustc.html"><strong><code>rustc</code></strong></a> to compile Rust Apps, then calls <strong>GCC Linker</strong> to link into our NuttX Firmware. Which doesnt seem to support LTO.</p>
<p>Well sort this out in GSoC!</p>
<p><a href="https://github.com/apache/nuttx/pull/5566">(Why NuttX calls <strong><code>rustc</code></strong> instead of <strong><code>cargo</code> <code>build</code></strong>)</a></p>
<p>(Which means we cant import Rust Crates from <a href="https://crates.io/"><strong><code>crates.io</code></strong></a>!)</p>
<h1 id="appendix-rust-build-for-64-bit-risc-v"><a class="doc-anchor" href="#appendix-rust-build-for-64-bit-risc-v">§</a>12 Appendix: Rust Build for 64-bit RISC-V</h1>
<p><em>We tested Rust Apps on QEMU for 32-bit RISC-V. What about 64-bit RISC-V?</em></p>
<p>Sorry Rust Apps wont build correctly on NuttX for 64-bit RISC-V…</p>
<div class="example-wrap"><pre class="language-bash"><code>$ tools/configure.sh rv-virt:nsh64
$ make menuconfig
## TODO: Enable &quot;Hello Rust Example&quot;
$ make
RUSTC: hello_rust_main.rs error: Error loading target specification:
Could not find specification for target &quot;riscv64i-unknown-none-elf&quot;.
Run `rustc --print target-list` for a list of built-in targets
make[2]: *** [nuttx/apps/Application.mk:275: hello_rust_main.rs...nuttx.apps.examples.hello_rust.o] Error 1
make[1]: *** [Makefile:51: nuttx/apps/examples/hello_rust_all] Error 2
make: *** [tools/LibTargets.mk:232: nuttx/apps/libapps.a] Error 2</code></pre></div>
<p>Which says that <em>riscv64i-unknown-none-elf</em> isnt a valid Rust Target.</p>
<p>(Should be <em>riscv64gc-unknown-none-elf</em> instead)</p>
<p>Well fix this in GSoC and test it on Ox64 BL808 SBC.</p>
<p>TODO: Test on QEMU Arm32 and Arm64</p>
<!-- Begin scripts/rustdoc-after.html: Post-HTML for Custom Markdown files processed by rustdoc, like chip8.md -->
<!-- Begin Theme Picker and Prism Theme -->
<script src="../theme.js"></script>
<script src="../prism.js"></script>
<!-- Theme Picker and Prism Theme -->
<!-- End scripts/rustdoc-after.html -->
</body>
</html>