Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to compile Rust code to bare metal 32 bit x86 (i686) code? What compile target should I use?

Tags:

rust

I'd like to compile bare metal 32-bit code for x86 (aka i686 aka x86_64 in 32-bit mode) using cargo/Rust. What target do I need? In the official supported target list I can't find anything, that is practical.

like image 640
phip1611 Avatar asked Sep 19 '25 03:09

phip1611


1 Answers

Unfortunately, Rust compiler doesn't have a built-in target definition [Rust nightly 1.54, June 2021] for this but you can provide a custom target definition:

<project-root>/x86-unknown-bare_metal.json

{
  "llvm-target": "i686-unknown-none",
  "data-layout": "e-m:e-i32:32-f80:128-n8:16:32-S128-p:32:32",
  "arch": "x86",
  "target-endian": "little",
  "target-pointer-width": "32",
  "target-c-int-width": "32",
  "os": "none",
  "executables": true,
  "linker-flavor": "ld.lld",
  "linker": "rust-lld",
  "panic-strategy": "abort",
  "disable-redzone": true,
  "features": "+soft-float,-sse"
}

This definition sets the pointer width to 32 and is meant to compile code, that could be used in the early x86 boot process (when you are already in 32-bit protected mode). I'm not 100% sure about the "features", because they may depend on what you want to do/need. i686 refers to x86_64 in 32-bit mode and the data-layout is explained here.

In addition, you should add the file

<project-root>/.cargo/config.toml

[unstable]
# cross compile core library for custom target
build-std = ["core", "compiler_builtins"]
build-std-features = ["compiler-builtins-mem"]

[build]
# points to file in project root
target = "x86-unknown-bare_metal.json"

Now you can build a bare metal binary with 32-bit x86 code using cargo build.

// disable rust standard library
#![no_std]
// disables Rust runtime init,
#![no_main]

// see https://docs.rust-embedded.org/embedonomicon/smallest-no-std.html
#![feature(lang_items)]

// see https://docs.rust-embedded.org/embedonomicon/smallest-no-std.html
#[lang = "eh_personality"]
extern "C" fn eh_personality() {}

use core::panic::PanicInfo;
use core::sync::atomic;
use core::sync::atomic::Ordering;

#[no_mangle]
/// The name **must be** `_start`, otherwise the compiler throws away all code as unused. 
/// The name can be changed by passing a different entry symbol as linker argument.
fn _start() -> ! {
    loop {}
}

#[inline(never)]
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
    loop {
        atomic::compiler_fence(Ordering::SeqCst);
    }
}

For convenience, you should also add

<project-root>/rust-toolchain.toml

# With this file, another toolchain to the currently selected one will be used, when you execute `cargo build`.
# https://rust-lang.github.io/rustup/overrides.html

[toolchain]
# equals to rust nightly 1.54 as of the release day 2021-05-10
channel = "nightly-2021-05-10"
components = [ "rust-src", "rust-std", "rustc", "cargo" ]
like image 179
phip1611 Avatar answered Sep 21 '25 11:09

phip1611