Build Integration
Use this page when you need to compile a Rust program against the installed SDK and produce a deployable binary.
Prerequisites
Section titled “Prerequisites”Start with Set Up Thru DevKit, then install the SDK:
thru dev toolchain installthru dev sdk install rustThe CLI installs the Rust SDK under ~/.thru/sdk/rust. Programs are compiled by cargo for riscv64imac-unknown-none-elf, and the raw binary is produced by cargo objcopy from cargo-binutils:
rustup target add riscv64imac-unknown-none-elfcargo install cargo-binutilsrustup component add llvm-toolsProject layout
Section titled “Project layout”A downstream project needs four things copied or referenced from the SDK: the two crates as path dependencies, build.rs, link.x.in, and .cargo/config.toml.
my-thru-project/├── .cargo/config.toml├── Cargo.toml├── build.rs├── link.x.in└── src/main.rsCargo.toml
Section titled “Cargo.toml”[package]name = "my-thru-project"version = "0.1.0"edition = "2021"
[dependencies]thru-core = { path = "../thru-rust-sdk/core" }thru-sdk-macros = { path = "../thru-rust-sdk/macros" }
[profile.release]panic = "abort"opt-level = "z"lto = truecodegen-units = 1debug = 2strip = falsepanic = "abort" is required: the SDK is no_std and provides its own panic handler. Keeping debug = 2 and strip = false preserves the DWARF info that thru debug resolve needs to map program counters back to source lines.
.cargo/config.toml
Section titled “.cargo/config.toml”[build]target = "riscv64imac-unknown-none-elf"
[target.riscv64imac-unknown-none-elf]rustflags = [ "-C", "target-feature=+crt-static", "-C", "target-feature=-a,+zba,+zbb,+zbc,+zbs", "-C", "link-arg=-Tlink.x",]The target features disable atomics and enable the bit-manipulation extensions the Thru VM supports. -Tlink.x selects the SDK linker script.
build.rs and link.x.in
Section titled “build.rs and link.x.in”Copy both from the SDK root unchanged. build.rs copies link.x.in into OUT_DIR as link.x and adds it to the link search path:
use std::{env, fs, path::PathBuf};
fn main() { let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); let dest_path = out_dir.join("link.x");
let content = fs::read_to_string("link.x.in").unwrap(); fs::write(dest_path, content).unwrap();
println!("cargo:rustc-link-search={}", out_dir.display()); println!("cargo:rerun-if-changed=link.x.in");}link.x.in emits the 8-byte Thru VM program header, then places .text._start and .text.start — the sections the #[entry] macro generates — at the start of the image. Do not reorder or drop those KEEP entries.
cargo build --release --bin my-thru-projectcargo objcopy --release --bin my-thru-project -- -O binary my-thru-project.binThe ELF lands in target/riscv64imac-unknown-none-elf/release/, and my-thru-project.bin is the raw image you deploy. Keep the ELF: it carries the debug info for symbolization.
Make integration
Section titled “Make integration”The SDK also ships a make wrapper for projects that prefer make over raw cargo. thru_rust_sdk.mk includes the base configuration, the Thru VM machine configuration (RUST_TARGET, CARGO_BUILD_FLAGS, CARGO_PROFILE_DIR), and the rule generator:
THRU_RUST_SDK_DIR := $(HOME)/.thru/sdk/rust/thru-sdkinclude $(THRU_RUST_SDK_DIR)/thru_rust_sdk.mkEvery Local.mk under the project is discovered automatically. Declare each program there, where the first argument is the output name and the second is the cargo example name — the make wrapper builds cargo examples, so program sources live in examples/:
$(call make-cargo-bin,my_thru_program,my_thru_program)make then writes both build/thruvm/bin/my_thru_program.bin and .elf. make clean and make distclean also run cargo clean.
Deploy
Section titled “Deploy”Deployment is the same as for C programs — create the program account from the .bin and execute against it. See Build a C Program for the CLI sequence and Program development lifecycle for the wider workflow.