背景
Rustには、最近RISC Vのターゲットが追加されたのでrustupでtargetをaddするだけで、動かせるように思うわけです。しかし対応しているものが(32bitで)riscv32imac/riscv32imcの2つに現状なっていて、自分の作ったコアがミニマムで、Compressedな命令(Extensionで言うところの"C"、16bit命令)を使われてしまうと、それに対応していなくてプログラムが動かせなくて困りました。
もっともLLVMには、Compressed命令ありでもなしでも動くように実装されているようなので、少なくともfreestandingなバイナリは、compressedなしでコンパイルできてもおかしくないという気持ちになります。ところで、ネットを探してもそのようなことをしている記事を見つけられなかったのでここにメモを残しておきます(もっとも、常識なのかもしれません)
結局以下のように単にターゲットのjsonを用意するだけで、Compressed命令なしで、バイナリを出すことができるようになりました。
方法
riscv-rust-hello をベースにします。RISCV向けに所々することは基本的にはこの記事 が参考になります。
https://github.com/japaric/rust-cross/blob/master/README.md#cross-compiling-no_std-code や https://os.phil-opp.com/minimal-rust-kernel/ を参考にすると、riscv32imaのtargetのspecが書かれたjsonファイルを作れれば良いことが言えます。またblog_os から、cargo xbuildを使うとcargoでクロスコンパイルさせることができ、良いことがわかります。
最後に https://doc.rust-lang.org/rustc/targets/custom.html によると
rustc +nightly -Z unstable-options --target=wasm32-unknown-unknown --print target-spec-json
のようにすれば、今存在するtargetのjsonが得られるのでこれをベースにfeaturesを書き換えると、お望みのriscv32imaが得られます。
結局既存のriscv32imac用のtarget specのjsonのうちfeatures部分を書き換えた以下のようなjsonを用いて、クロスコンパイルすると動くようになりました。
{
"abi-blacklist": [
"cdecl",
"stdcall",
"fastcall",
"vectorcall",
"thiscall",
"aapcs",
"win64",
"sysv64",
"ptx-kernel",
"msp430-interrupt",
"x86-interrupt",
"amdgpu-kernel"
],
"arch": "riscv32",
"atomic-cas": false,
"cpu": "generic-rv32",
"data-layout": "e-m:e-p:32:32-i64:64-n32-S128",
"emit-debug-gdb-scripts": false,
"env": "",
"executables": true,
"features": "+m,+a",
"is-builtin": true,
"linker": "rust-lld",
"linker-flavor": "ld.lld",
"llvm-target": "riscv32",
"max-atomic-width": 32,
"os": "none",
"panic-strategy": "abort",
"relocation-model": "static",
"target-c-int-width": "32",
"target-endian": "little",
"target-pointer-width": "32",
"vendor": "unknown"
}