Compile for riscv32im-unknown-none-elf

I need to write code for a riscv machine that currently has no support for compressed instructions. Apparently, Rust only supports riscv32i, riscv32imc and riscv32imac, but not riscv32im.

I found a reddit post with the same problem and the accepted answer suggested using the target-feature compile option, but without further instructions. I thus modified my .cargo/config to look like this:

[target.riscv32i-unknown-none-elf]
rustflags = [
  "-C", "link-arg=-Tmemory.x",
  "-C", "link-arg=-Tlink.x",
  "-C", "target-feature=m",
]

[build]
target = "riscv32i-unknown-none-elf"

But as far as I can tell, this had no effect. I also found a blog post that tells me that I need to build Rust from the target from scratch, but the further links for details didn't help me out (probably the relevant part is in Japanese).

Okay, I found out. My approach was mostly correct, however the code didn't update because of some weird caching. The general syntax is more powerful, it can support +f or -f to enable/disable features and multiple features can be comma-separated. However, I tried building for riscv32imc with -c as flag and that didn't work, it still generated compressed instructions. Also, with my +m there are still some __mulsi3 that are used, but as long as the compiler also generated true mul and mulh instructions I won't bother with that for now.

One can go further though and create a completely custom configuration. These are simple json files, see here and here for more usage instructions. However, as far as I can tell one then needs to compile libcore against that new target, and I decided not to check the depth of that rabbit hole.

You need to compile the standard library with -c too. You can for example use cargo build -Zbuild-std=core for that on nightly I think.

1 Like

My best hypothesis for that right now is that Rust applies the compiler flag, but only to newly compiled code. This especially excludes libcore, since it is not built from scratch. So in the end the option only applies to my code+dependencies, but not to e.g. the panic handler or the startup routine.

I've managed to compile a custom libcore (actually is it possible without nightly?), but I don't know how to put it to use. When I use the previous target-feature=-c option, it seems to be ignored. When I use my custom target, I get 'can't find crate for core':

  |
  = note: the `riscv32im-unknown-none-elf-13461130471460371938` target may not be installed
  = help: consider downloading the target with `rustup target add riscv32im-unknown-none-elf-13461130471460371938`
  = help: consider building the standard library from source with `cargo build -Zbuild-std`

(Note: my target is called "riscv32im-unknown-none-elf.json", and I don't know where that number is coming from). I tried both with --target=riscv32im-unknown-none-elf.json and setting it in ./cargo/config.

You need to build your project with cargo build -Zbuild-std=core. This will compile libcore and use this new libcore to compile your project. It is currently not possible to build libcore yourself on stable and in the future once it stabilizes -Zbuild-std will be the only stable way.

Ah I misunderstood, I have to always give that flag. Now it works, thanks. (Okay, the custom target works, the others still don't appear to be applying the config I gave them for libcore even with -Zbuild-std=core. But it doesn't matter anymore.)

Slightly different topic: is there a way to not having to always specify -Zbuild-std=core? It's getting tedious. I looked for related settings in Cargo.toml, ./cargo/config and build.rs, but I may have missed something (also, I didn't check possible environment variables).

You can put

[unstable]
build-std = ["core", "compiler_builtins"]

in .cargo/config.toml. You can also put

[build]
target = "riscv32im-unknown-none-elf.json"

there too to compile for your target by default.

1 Like