mirror of
https://github.com/lupyuen/lupyuen.github.io.git
synced 2025-01-13 09:08:30 +08:00
795 lines
No EOL
51 KiB
HTML
795 lines
No EOL
51 KiB
HTML
<!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="What’s Next">10 What’s 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 it’s 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 we’re 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 that’s 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>) -> i32; <span class="comment">// Returns `int`
|
||
</span>} <span class="comment">// TODO: Standardise `i32` as `c_int`</span></code></pre></div>
|
||
<p>(We’ll 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>) -> 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">&</span>PanicInfo<<span class="lifetime">'_</span>> <span class="comment">// Receives the Panic Info and Stack Trace
|
||
</span>) -> ! { <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 doesn’t do much right now. We’ll 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> > <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> > <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> > <strong>Examples</strong>”</p>
|
||
<p>Select “<strong>Hello Rust Example</strong>”</p>
|
||
<p>Select it <strong>Twice</strong> so that “<strong><code><M></code></strong>” changes to “<strong><code><*></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 \
|
||
>nuttx.S \
|
||
2>&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>“Can’t 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'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 's/00000024: 00/00000024: 04/' \
|
||
| 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>(We’ll 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>We’re 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></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> 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> help
|
||
help usage: help [-v] [<cmd>]
|
||
|
||
. 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 won’t 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>We’ll 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>We’ve 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, ...) -> i32;
|
||
<span class="kw">pub fn </span>puts(s: <span class="kw-2">*const </span>c_char) -> 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) -> <span class="kw-2">*mut </span>c_char;
|
||
<span class="kw">pub fn </span>lib_get_stream(fd: c_int) -> <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) -> 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">&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">&</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 we’re 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">&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">&mut </span>buffer)<span class="question-mark">?</span>;</code></pre></div>
|
||
<p>But this won’t work on NuttX because…</p>
|
||
<ul>
|
||
<li>
|
||
<p><strong>Rust Standard Input</strong> <em>io::stdin()</em> isn’t supported on Embedded Platforms</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>Dynamic Strings and Heap Memory</strong> won’t 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>. We’ll 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, let’s talk about the hiccups…</p>
|
||
<p><img src="https://lupyuen.github.io/images/rust3-float.png" alt="Can’t 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>What’s this error? “Can’t 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'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 's/00000024: 00/00000024: 04/' \
|
||
| 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 'i' 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: There’s 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! Here’s 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>What’s 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':
|
||
apps/examples/hello_rust/hello_rust_main.rs:90:
|
||
undefined reference to `core::panicking::panic'</code></pre></div>
|
||
<p>Suppose we’re 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">&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>What’s 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 we’re 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 doesn’t seem to support LTO.</p>
|
||
<p>We’ll 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 can’t 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. Doesn’t support Heap Memory and POSIX.</p>
|
||
</li>
|
||
</ul>
|
||
<p>The <em>malloc()</em> that we mentioned: It’s 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>: We’ll use the <strong>Rust Core Library</strong>. Which doesn’t support Heap Memory and doesn’t need <em>malloc()</em>.</p>
|
||
<p><em>But most Kernel Drivers will need Kernel Heap!</em></p>
|
||
<p>That’s 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> We’ll 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 we’re calling <strong>Rust Core Library</strong> in NuttX Kernel, we won’t touch any POSIX Application Interfaces. Thus if we need to support the Kernel Equivalent of Errno (and other Global State), we’ll 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 it’s 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 that’s super keen on Rust)</p>
|
||
<p>— Hence we’re 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 we’re 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>(We’ll 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 we’re 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 we’re <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 What’s 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. We’ll 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 wouldn’t 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>What’s 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':
|
||
apps/examples/hello_rust/hello_rust_main.rs:90:
|
||
undefined reference to `core::panicking::panic'</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">&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':
|
||
apps/examples/hello_rust/hello_rust_main.rs:90:
|
||
undefined reference to `core::panicking::panic'</code></pre></div>
|
||
<p><em>What’s 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 we’re 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 doesn’t seem to support LTO.</p>
|
||
<p>We’ll 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 can’t 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 won’t 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 "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
|
||
|
||
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> isn’t a valid Rust Target.</p>
|
||
<p>(Should be <em>riscv64gc-unknown-none-elf</em> instead)</p>
|
||
<p>We’ll 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> |