Removing appended "break" instruction in inline mips assembly - rust

I have the following (simplified) function using inline assembly, targeting mips:
#[naked]
pub unsafe extern "C" fn test() {
asm!(
".set noreorder",
"jr $ra",
"li $v0, 0x123",
options(noreturn),
)
}
I expected this to compile into just the 2 specified instructions (in release mode), as it's a naked function, but a break instruction gets appended at the end:
00000000 <test>:
0: 03e00008 jr ra
4: 24020123 li v0,291
8: 0000000d break
I assume this is a countermeasure against undefined behavior by either rustc or llvm, but I need to product the exact assembly I specify in the function.
Is there any way to prevent either rustc, llvm or the assembler from generating this extra instruction generally?
I tested it on existing targets such as mipsel-unknown-none and it also produced a break instruction, but I am compiling on the following custom target, if it matters:
{
"arch": "mips",
"cpu": "mips1",
"data-layout": "e-m:m-p:32:32-i8:8:32-i16:16:32-i32:32-n32-S32",
"emit-debug-gdb-scripts": false,
"executables": false,
"features": "+mips32,+soft-float,+noabicalls",
"linker": "rust-lld",
"linker-flavor": "ld.lld",
"llvm-target": "mipsel-unknown-linux-gnu",
"relocation-model": "static",
"target-pointer-width": "32",
"panic-strategy": "abort",
"singlethread": true,
"dynamic-linking": false,
"function-sections": true
}
I'm also using a #![no_std] and #![no_core] staticlib crate with the required lang items implemented and simply compiling using cargo build --release --target=my-target.json
Edit: After Peter Cordes's suggestion, I tried the same in C with
__attribute__((naked)) void test() {
__asm__(
".set noreorder\n"
"jr $ra\n"
"li $v0, 0x123\n"
);
}
Compiled using
clang -O3 test.c -c -o test.o -target mips-unknown-none
And the result is
00000000 <test>:
0: 03e00008 jr ra
4: 24020123 li v0,291
Without a break, so it seems it was included by the rust compiler.

Yes! Do one of:
Add "trap_unreachable": false to your target.json
Build with RUSTFLAGS=-Ztrap-unreachable=no. (nightly-only though)
Unfortunately it's not very well documented. Further reading: PR where the trap instruction generation was added PR where trap-unreachable=no was added

Related

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

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.
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" ]

Why am I getting a VFP register arguments while adding a custom target to rust (arm-unknown-linux-uclibceabihf)?

I am following the guide from the last rust newsletter: https://rust-embedded.github.io/embedonomicon/custom-target.html and encounter an issue related to soft/hard float configuration.
I created my new target by merging the mipsel-unknown-linux-uclibc.json with the arm-unknown-linux-gnueabihf.json to obtain the following result:
{
"arch": "arm",
"data-layout": "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64",
"dynamic-linking": true,
"env": "uclibc",
"executables": true,
"features": "+strict-align,+v6,+vfp2",
"has-elf-tls": true,
"has-rpath": true,
"is-builtin": false,
"linker-flavor": "gcc",
"linker-is-gnu": true,
"linker": "/home/ykoehler/work/tools/arm-5.3-uclibc-1.0.12/usr/bin/arm-buildroot-linux-uclibcgnueabihf-gcc",
"llvm-target": "arm-unknown-linux-uclibceabihf",
"max-atomic-width": 64,
"os": "linux",
"position-independent-executables": true,
"pre-link-args": {
"gcc": [
"-Wl,--as-needed",
"-Wl,-z,noexecstack"
]
},
"relro-level": "full",
"target-c-int-width": "32",
"target-endian": "little",
"target-family": "unix",
"target-mcount": "_mcount",
"target-pointer-width": "32",
"vendor": "unknown"
}
When I compile a no-std app with cargo using the new custom target:
cargo build -Z build-std=core --target arm-unknown-linux-uclibceabihf.json
I am getting the following error:
Compiling app v0.1.0 (/home/ykoehler/work/app)
error: linking with `/home/ykoehler/work/tools/arm-5.3-uclibc-1.0.12/usr/bin/arm-buildroot-linux-uclibcgnueabihf-gcc` failed: exit code: 1
|
= note: "/home/ykoehler/work/tools/arm-5.3-uclibc-1.0.12/usr/bin/arm-buildroot-linux-uclibcgnueabihf-gcc" "-Wl,--as-needed" "-Wl,-z,noexecstack" "-Wl,--eh-frame-hdr" "-L" "/home/ykoehler/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/arm-unknown-linux-uclibceabihf/lib" "/home/ykoehler/work/app/target/arm-unknown-linux-uclibceabihf/release/deps/app-92459304df1651f0.app.2jjler86-cgu.0.rcgu.o" "-o" "/home/ykoehler/work/app/target/arm-unknown-linux-uclibceabihf/release/deps/app-92459304df1651f0" "-Wl,--gc-sections" "-pie" "-Wl,-zrelro" "-Wl,-znow" "-Wl,-O1" "-nodefaultlibs" "-L" "/home/ykoehler/work/app/target/arm-unknown-linux-uclibceabihf/release/deps" "-L" "/home/ykoehler/work/app/target/release/deps" "-L" "/home/ykoehler/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/arm-unknown-linux-uclibceabihf/lib" "-Wl,-Bstatic" "/home/ykoehler/work/app/target/arm-unknown-linux-uclibceabihf/release/deps/librustc_std_workspace_core-375ea25c872246b5.rlib" "/home/ykoehler/work/app/target/arm-unknown-linux-uclibceabihf/release/deps/libcore-34e07d1b528fc142.rlib" "/home/ykoehler/work/app/target/arm-unknown-linux-uclibceabihf/release/deps/libcompiler_builtins-a34a2e58700fb5e5.rlib" "-Wl,-Bdynamic"
= note: /home/ykoehler/work/tools/arm-5.3-uclibc-1.0.12/usr/bin/../lib/gcc/arm-buildroot-linux-uclibcgnueabihf/5.3.0/../../../../arm-buildroot-linux-uclibcgnueabihf/bin/ld: error: /home/ykoehler/work/app/target/arm-unknown-linux-uclibceabihf/release/deps/app-92459304df1651f0 uses VFP register arguments, /home/ykoehler/work/app/target/arm-unknown-linux-uclibceabihf/release/deps/app-92459304df1651f0.app.2jjler86-cgu.0.rcgu.o does not
/home/ykoehler/work/tools/arm-5.3-uclibc-1.0.12/usr/bin/../lib/gcc/arm-buildroot-linux-uclibcgnueabihf/5.3.0/../../../../arm-buildroot-linux-uclibcgnueabihf/bin/ld: failed to merge target specific data of file /home/ykoehler/work/app/target/arm-unknown-linux-uclibceabihf/release/deps/app-92459304df1651f0.app.2jjler86-cgu.0.rcgu.o
/home/ykoehler/work/tools/arm-5.3-uclibc-1.0.12/usr/arm-buildroot-linux-uclibcgnueabihf/sysroot/usr/lib/Scrt1.o: In function `_start':
/var/tmp/tool-chains/buildroot-2016.02-5.3-arm/src/buildroot-2016.02/output/build/uclibc-1.0.12/libc/sysdeps/linux/arm/crt1.S:217: undefined reference to `__uClibc_main'
/var/tmp/tool-chains/buildroot-2016.02-5.3-arm/src/buildroot-2016.02/output/build/uclibc-1.0.12/libc/sysdeps/linux/arm/crt1.S:235: undefined reference to `abort'
/var/tmp/tool-chains/buildroot-2016.02-5.3-arm/src/buildroot-2016.02/output/build/uclibc-1.0.12/libc/sysdeps/linux/arm/crt1.S:235: undefined reference to `main'
collect2: error: ld returned 1 exit status
error: aborting due to previous error
error: could not compile `app`.
To learn more, run the command again with --verbose.
This error indicate that some code is compiled with soft float usage when other part is using hard float, from reading over the net about the error message. Yet, both files specified by the error appears to have been generated by rust, and I am unclear why rust would generate any soft float code based on the information I provided inside the custom target definition file.
Any help appreciated.
I believe you just need to use arm-unknown-linux-gnueabihf for the llvm-target. The VFP error is related to the floating-point instruction set available (i.e. the hf in eabihf stands for Hard Float). I'm working on getting the armv7-unknown-linux-uclibceabihf target working in rust. I received the same VFP error while getting the armv7-unknown-linux-uclibceabihf target to work in rust and it turned out I just needed to use armv7-unknown-linux-gnueabihf for the llvm-target.
Also, here's my spec file for armv7-unknown-linux-uclibceabihf for comparison:
{
"abi-blacklist": [
"stdcall",
"fastcall",
"vectorcall",
"thiscall",
"win64",
"sysv64"
],
"arch": "arm",
"data-layout": "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64",
"dynamic-linking": true,
"env": "uclibc",
"executables": true,
"features": "+v7,+vfp3,-d32,+thumb2,-neon",
"has-elf-tls": true,
"has-rpath": true,
"linker-flavor": "gcc",
"linker-is-gnu": true,
"llvm-target": "armv7-unknown-linux-gnueabihf",
"max-atomic-width": 64,
"os": "linux",
"position-independent-executables": true,
"pre-link-args": {
"gcc": [
"-Wl,--as-needed",
"-Wl,-z,noexecstack"
]
},
"relro-level": "full",
"target-c-int-width": "32",
"target-endian": "little",
"target-family": "unix",
"target-mcount": "\u0001__gnu_mcount_nc",
"target-pointer-width": "32",
"vendor": "unknown"
}
Cheers

unknown feature `llvm_asm` when compile rust-src

I tried to compile rust-src using cargo xbuild but get this error:
error[E0635]: unknown feature `llvm_asm`
-> .cargo/registry/src/github.com-1ecc6299db9ec823/compiler_builtins-0.1.28/src/lib.rs:3:12
3 | #![feature(llvm_asm)]
How can I fix this error? It's seem like xbuild tries to compile the new rust-src with an old rustc. I want it to also use the old rust-src.
I can't update to a newer rustc version as it results in lots of "R_x86_32 relocation" errors, so I would prefer to use the 2020-03-24 version.
Minimal example
command
cargo new --bin test
rustup component add rust-src
cargo install cargo-xbuild
cd test
ls test
Cargo.toml rust-toolchain src x86_64-unknown-none.json
rust-toolchain
nightly-2020-03-24
x86_64-unknown-none.json
{
"llvm-target": "x86_64-unknown-none",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",
"target-c-int-width": "32",
"os": "none",
"executables": true,
"linker-flavor": "ld.lld",
"linker": "rust-lld",
"panic-strategy": "abort",
"disable-redzone": true,
"features": "-mmx,-sse,+soft-float"
}
src/main.rs
#![no_std] // don't link the Rust standard library
#![no_main] // disable all Rust-level entry points
#![allow(non_snake_case)] // disable non snake case name warning
use core::panic::PanicInfo;
#[no_mangle]
pub extern "C" fn _start() -> ! {
loop {}
}
#[panic_handler]
pub fn MyPacnicHandler(_panicInfo: &PanicInfo) -> ! {
loop {}
}
compile
cargo xbuild --target x86_64-unknown-none
rustc --version
rustc 1.44.0-nightly (1edd389cc 2020-03-23)
This is a bug in cargo-xbuild. Basically, cargo xbuild unconditionally fetches the latest compiler_builtins.
A patch has been merged, but is not yet in the latest crates.io release. See this PR: https://github.com/rust-osdev/cargo-xbuild/pull/75/commits/eede1a1d4c08064763f1943c0920de2270260b33
Update your rust version by rustup update, which works for me.
The reason may be the feature rename in new version : https://github.com/rust-lang/rust/pull/71007

`xargo build` cannot find library

So I'm following this tutorial on how to create a VERY BASIC Operating System using the Rust programming language. (I intend on buying an actual book on the subject, but I'm using this for now).
Here are some files we created just to clarify things a little:
Cargo.toml
[package]
name = "blog_os"
version = "0.1.0"
authors = ["Philipp Oppermann <dev#phil-opp.com>"] # Here I used my own details
[lib]
crate-type = ["staticlib"]
src/lib.rs
#![feature(lang_items)]
#![no_std]
#[no_mangle]
pub extern fn rust_main() {}
#[lang = "eh_personality"] extern fn eh_personality() {}
#[lang = "panic_fmt"] #[no_mangle] pub extern fn panic_fmt() -> ! {loop{}}
x86_64-blog_os.json
{
"llvm-target": "x86_64-unknown-none",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"linker-flavor": "gcc",
"target-endian": "little",
"target-pointer-width": "64",
"target-c-int-width": "32",
"arch": "x86_64",
"os": "none",
"disable-redzone": true,
"features": "-mmx,-sse,+soft-float"
}
If you scroll down to the Compiling section in the tutorial, the author shows us how to install xargo and how to use it to build.
We're then asked to run:
> xargo build --target=x86_64-blog_os
But when I do, I get the following error message:
error: failed to parse manifest at '/home/max/TesterOS/src/Cargo.toml'
Caused by:
can't find library 'blog_os', rename file to 'src/lib.rs' or specify lib.path
Is the issue connected to where I saved my files? Because I followed the tutorial down to the letter, but the author wasn't specific in where everything should be saved.
SOLVED: Turns out it was actually related to where I placed my files.
I had to create a blog_os folder and stored my files in there. Hence the error:
can't find library 'blog_os'....
Rookie mistake :)

How to obtain the value of a configuration flag?

Is there a way of obtaining the value of a configuration flag? For example, I would like to get the value of target_os as str/String, without resorting to the following if-else-if chain:
if cfg!(target_os = "windows") {
"windows"
} else if cfg!(target_os = "linux") {
"linux"
// ...
} else {
"unknown"
}
No. You can get some of them by tricking Cargo into telling you. If you place the following into a build script:
use std::env;
fn main() {
for (key, value) in env::vars() {
if key.starts_with("CARGO_CFG_") {
println!("{}: {:?}", key, value);
}
}
panic!("stop and dump stdout");
}
...it will display the cfg flags Cargo is aware of. The panic! is just there as an easy way to get Cargo to actually show the output instead of hiding it. For reference, the output this produces looks like this:
Compiling dump-cfg v0.1.0 (file:///F:/Programming/Rust/sandbox/cargo-test/dump-cfg)
error: failed to run custom build command for `dump-cfg v0.1.0 (file:///F:/Programming/Rust/sandbox/cargo-test/dump-cfg)`
process didn't exit successfully: `F:\Programming\Rust\sandbox\cargo-test\dump-cfg\target\debug\build\dump-cfg-8b04f9ac3818f82a\build-script-build` (exit code: 101)
--- stdout
CARGO_CFG_TARGET_POINTER_WIDTH: "64"
CARGO_CFG_TARGET_ENV: "msvc"
CARGO_CFG_TARGET_OS: "windows"
CARGO_CFG_TARGET_ENDIAN: "little"
CARGO_CFG_TARGET_FAMILY: "windows"
CARGO_CFG_TARGET_ARCH: "x86_64"
CARGO_CFG_TARGET_HAS_ATOMIC: "16,32,64,8,ptr"
CARGO_CFG_TARGET_FEATURE: "sse,sse2"
CARGO_CFG_WINDOWS: ""
CARGO_CFG_TARGET_VENDOR: "pc"
CARGO_CFG_DEBUG_ASSERTIONS: ""
--- stderr
thread 'main' panicked at 'stop', build.rs:9
note: Run with `RUST_BACKTRACE=1` for a backtrace.
You can extract the values you're interested in from this list, and dump them to a generated source file, which you can then import (using #[path] or include!) into your package's source.
For target_os specifically, and also for just target_family and target_arch, there are corresponding &str constants in std::env::consts::{OS, FAMILY, ARCH}.

Resources