Main symbol is not in binary in risc-v (tier-2) no_std no_main target?


I'm trying to build a small riscv program, and I'm finding that main isn't in the output binary. Is there some way I need to tell rust or the linker not to strip it?

My src/ is this:


pub extern "C" fn main() -> i32 {

#[cfg(target_arch = "riscv32")]
fn panic(_info: &core::panic::PanicInfo) -> ! {
    loop {}

My Cargo.toml is:

name = "riscv-example"
version = "0.1.0"
edition = "2021"

I'm running this to check it (and only getting debug symbols):

cargo clean && cargo build --target riscv32i-unknown-none-elf \
  && nm target/riscv32i-unknown-none-elf/debug/riscv-example

I don't think that the symbol is not exported, it's probably the linker discarded it from the final artifact. you didn't show your linker script so I can't be sure. you can try the following:

  • check the symbols in the object file, see if main is there
  • check the entry point of the final elf file, then trace the call chain to see if your main is reached in some code path
  • check the sections of the elf file, specifically, whether the .text section is discarded

the object file might not be easy to locate, it is usually somewhere under the "target/[target-triplet-name]/debug/incremental" directory, some times under "target/[target-triplet-name]/debug/build/<crate-name>", I don't know exactly how the file name is generated, I just try all the '.o' objects there.

The #![no_main] attribute in your code tells the compiler that you won't be using the standard main function, so you will need to provide one yourself.

On RISC-V, you can use the entry attribute from the riscv-rt crate. An example is provided below:


use riscv_rt::entry;

// ...

fn main() -> ! {

// ...

A complete code example is given here:

I was wondering if I needed a linker script, as I expected the symbol was being stripped - but didn't want to make that assumption and mislead in my question.

I don't have a linker script currently. Do you know what a better supported target architecture's linker script may look like? I'm not familiar with linker scripts, and haven't needed them with the better supported targets I've used previously.

For interest, yes this does contain main, so it looks like it is being stripped. Thanks!

nm target/riscv32i-unknown-none-elf/debug/incremental/riscv_example-1ovpohybuzp4d/s-gklnmqo6sl-rxujpz-brz8p38iuzk0/4hsyi017o6h87y81.o

I had a "main" symbol, but wasn't sure of the idiomatic way to mark it as an entry point, or prevent it being stripped. All the ways I found seemed to require std or didn't seem to work on risc-v, because of some missing machinery like a linker script. I'll take a look into how riscv_rt does it, and report back if I get it working. Thank you!

Perfect! Thank you, that works, I think it's heavier than I need, but I can probably extract the necessary parts (#[export_name = "main"], parts of memory.x and link.x).

It was the need for a linker script in the end, thank you also @nerditation!