Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to generate one instruction only when asm! is used in naked functions? #96037

Closed
luojia65 opened this issue Apr 14, 2022 · 8 comments
Closed
Labels
A-inline-assembly Area: Inline assembly (`asm!(…)`) I-heavy Issue: Problems and improvements with respect to binary size of generated code. O-riscv Target: RISC-V architecture T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@luojia65
Copy link
Contributor

luojia65 commented Apr 14, 2022

There's a usage sceneario in Rust for embedded rom bootloader. Some chips use eGON.BT0 format on boot, it has one jump instruction on head and the following should be boot tables.

Bit index Content
0..4 an instruction j main
5..12 bootloader magic
12.. following boot configurations

It's ideal to write this instruction j main using Rust naked function as follows, in this way we do not require further tools to help with filling this instruction.

#[naked]
#[link_section = ".head.text"]
#[export_name = "head_jump"]
pub unsafe extern "C" fn head_jump() {
    asm!("j {}", sym main, options(noreturn))
}

The options(noreturn) is required in current naked function or it won't compile. I expect this function to generate one instruction j main only, however it generates an unimp which is unexpected:

Disassembly of section .head.text:

0000000000000000 <head_jump>:
       0: 6f 00 60 02   j       0x26 <_ZN18test_d1_flash_bare4main17h5fd8eeda800e7085E>
       4: 00 00         unimp

Disassembly of section .text:

...

I didn't expect this unimp instruction, for it will overlap bootloader magic, making this Rust image not bootable. What should I do?

I looked into RFC 2972, in section Naked Function Definition it shows:

  1. emit no additional instructions to the function body before the asm!() statement.

Should we expect that there would be additional instructions after what we have in asm! macro?

Note: I try to remove the option(noreturn), it shows following compile error:

   Compiling test-d1-flash-bare v0.1.0 (D:\RustSBI\test-d1-flash-bare)
error[E0787]: asm in naked functions must use `noreturn` option
  --> src\main.rs:17:5
   |
17 |     asm!("j {}", sym start)
   |     ^^^^^^^^^^^^^^^^^^^^^^^
   |
help: consider specifying that the asm block is responsible for returning from the function
   |
17 |     asm!("j {}", sym start, options(noreturn))
   |                           +++++++++++++++++++

For more information about this error, try `rustc --explain E0787`.
error: could not compile `test-d1-flash-bare` due to previous error
@hellow554
Copy link
Contributor

hellow554 commented Apr 14, 2022

It could/should work with global_asm!, but sym is currenlty not compatible with that. But there's a PR @ #94468
Maybe you could give it a try.

@jyn514 jyn514 added O-Arm Target: 32-bit Arm processors (armv6, armv7, thumb...), including 64-bit Arm in AArch32 state A-inline-assembly Area: Inline assembly (`asm!(…)`) T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. I-heavy Issue: Problems and improvements with respect to binary size of generated code. labels Apr 26, 2023
@jyn514
Copy link
Member

jyn514 commented Apr 26, 2023

Triage: is this still an issue? I'm not sure what --target you're compiling to so I can't check myself locally.

eGON.BT0 seems to be some ARM chip: https://linux-sunxi.org/Main_Page

@jyn514
Copy link
Member

jyn514 commented Apr 26, 2023

Ok, turns out this is a MIPS architecture actually. Minimal reproduction:

#![no_std]
#![feature(naked_functions, asm_experimental_arch, start)]

use core::arch::asm;

#[naked]
#[link_section = ".head.text"]
#[export_name = "head_jump"]
pub unsafe extern "C" fn head_jump() {
    asm!("j {}", sym main, options(noreturn))
}

#[start]
fn start(_: isize, _: *const *const u8) -> isize {
    0
}

fn main() {}

#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
    loop {}
}

I'm trying to get access to a linker/version of objdump that actually supports this architecture, I think binutils-mips-linux-gnu is what I want.

@jyn514 jyn514 added the O-MIPS Target: MIPS processors label Apr 26, 2023
@luojia65
Copy link
Contributor Author

@jyn514 : I'm using RISC-V compiling target. It's riscv64imac-unknown-none-elf.

@jyn514 jyn514 added O-riscv Target: RISC-V architecture and removed O-Arm Target: 32-bit Arm processors (armv6, armv7, thumb...), including 64-bit Arm in AArch32 state O-MIPS Target: MIPS processors labels Apr 26, 2023
@jyn514
Copy link
Member

jyn514 commented Apr 26, 2023

Hmm, I'm still having trouble reproducing this. I'm compiling with rustc example.rs -C panic=abort --target riscv64imac-unknown-none-elf and disassembling with riscv64-unknown-elf-objdump -D example, but it doesn't have any .head.text section:

; riscv64-unknown-elf-objdump -D example -j .head.text

example:     file format elf64-littleriscv

riscv64-unknown-elf-objdump: section '.head.text' mentioned in a -j option, but not found in any input file

What am I missing?

@luojia65
Copy link
Contributor Author

@jyn514 : It looks like that we should provide a linker script to include ".head.text" into output file, or I'll try put it into ".text" instead to see what will happen.

@erikdesjardins
Copy link
Contributor

Godbolt repro: https://godbolt.org/z/d9oebcjhW

This happens because of TrapUnreachable, which tells LLVM to codegen unreachable as a trap instruction instead of nothing:

if (TrapUnreachable) {
// Tell LLVM to codegen `unreachable` into an explicit trap instruction.
// This limits the extent of possible undefined behavior in some cases, as
// it prevents control flow from "falling through" into whatever code
// happens to be laid out next in memory.
Options.TrapUnreachable = true;
}

Given that TrapUnreachable significantly limits the impact of returning-from-noreturn (and similar "entering unreachable code") UB, and that global_asm! works (see godbolt link above), I don't think we should make any change here.

@tgross35
Copy link
Contributor

tgross35 commented Jan 9, 2025

Since #128004 naked functions much now use core::arch::naked_asm!, which gets treated as global assembly. This no longer reproduces https://godbolt.org/z/d1j34Wd6v:

head_jump:
        j       example::main::h71184965de29500a

example::main::h71184965de29500a:
        ret

@tgross35 tgross35 closed this as completed Jan 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-inline-assembly Area: Inline assembly (`asm!(…)`) I-heavy Issue: Problems and improvements with respect to binary size of generated code. O-riscv Target: RISC-V architecture T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

5 participants