最近、glibc本家に32bit RISC-Vのサポートがマージされていることに気付いた( https://sourceware.org/git/?p=glibc.git;a=commit;h=72dfddeffcc993a726bdcbe5e515afa1180095e8 )。64bit RISC-Vのサポートは以前から有ったけど、WASMが32bitなので32bit環境の方が食べ合せが良いかなということで。
今回は、buildrootとcrosstool-ngでRISC-Vネイティブ開発環境を用意し、qemuからVirtfs(9Pfs-root)で起動することで ファイルシステムイメージなしで (= ホストのファイルシステムから直接起動する)開発環境を用意することができた。
さくせん
yoctoで作っても良いかなと思ったけどちょっと記事に書くほどは理解度が足りてないので古き良きbuildrootとcrosstool-ngの組合せで行くことに。どちらも menuconfig
で項目をポチポチ選ぶだけで任意のオプション入りSDKを作ることができ初心者におすすめできる。
buildroot ( https://github.com/buildroot/buildroot ) は、組込み用Linuxディストリビューションをを作るためのツールで、Linuxカーネルと busybox
、いくつかのパッケージを事前導入した典型的な組込みLinuxシステムを簡単に作ることができる。ただし、 buildrootは 意図的にターゲット上で動作するコンパイラ等を排除している (メンテが大変だから) ため、開発環境は別途調達する必要がある。
crostool-ng は、以前 ESP32の開発環境を用意したとき にも登場したツールチェインのビルドシステムで、今回は "カナディアン・クロス" ビルド (クロスコンパイラをクロスコンパイルすること)によって、amd64 Linux上でRISC-V32上で動作するRISC-V32用のツールチェーンをビルドする。
ターゲットRISC-V ABIおよび命令セット
今回は、 rv32imafd
命令セットと ilp32d
ABIをターゲットする。それぞれ、
-
rv32imafd
=RV32I
(Base Integer Instruction Set) +M
(Multiplication) +A
(Atomic) +F
(Single-precision floating-point) +D
(Double-precision floating-point) -
ilp32d
=int
long
pointer は各32bit、関数の引数渡に浮動小数点レジスタを活用(d
)
という意味で、 GCCのマニュアル で言及されている。
命令セットはともかく、ABIの選択肢はさらにglibcのサポート範囲でも限定される。 glibcのコミットのdiff によると、
--- a/sysdeps/unix/sysv/linux/riscv/Makefile
+++ b/sysdeps/unix/sysv/linux/riscv/Makefile
@@ -7,11 +7,13 @@ ifeq ($(subdir),stdlib)
gen-as-const-headers += ucontext_i.sym
endif
-abi-variants := lp64 lp64d
+abi-variants := ilp32 ilp32d lp64 lp64d
今のところ ABI は ilp32 ilp32d lp64 lp64d
の4択になっている。
用途
今回ビルドした開発環境はGitHubのリポジトリに収まるサイズにしている。
前回の記事 から類推できるように、最終的にはこのリポジトリをWebブラウザから直接読み取って動作するRISC-V開発環境を用意したい。既にブラウザ上で動作する優秀なRISC-Vエミュレータとして BelladのTinyEMU があるので、あとはファイルさえ用意すれば行けるんじゃないかと予想している。
buildrootによるLinux環境の準備
buildroot は単純にリポジトリ( https://github.com/buildroot/buildroot )をチェックアウトし、
make qemu_riscv32_virt_defconfig
make menuconfig # 適当に欲しいパッケージを指定
make
とすればビルドできる。このとき、
-
output/images/fw_jump.bin
- qemuの-bios
オプションに指定するブートローダー -
output/images/Image
- Linuxカーネル本体 -
output/images/rootfs.tar
- rootfs
の各ファイルが出力となる。 rootfs.tar
はどこか適当なところに展開しておく。
crosstool-ngによるSDKの準備
buildrootで作ったファイルシステムにはコンパイラ類が含まれていないので、それらは crosstool-ng で用意する必要がある。こちらはちょっと設定項目が多い。
menuconfig の準備
buildroot と異なり、crosstool-ngは事前にビルドする必要がある。リポジトリ( https://github.com/crosstool-ng/crosstool-ng )をチェックアウトしたら、
./bootstrap
./configure --enable-local
make
./ct-ng menuconfig
のように ct-ng
をビルドしてから menuconfig
を起動する必要がある。
Experimental
RISC-V はbuildrootのExperimental featureなので、明示的に有効化する必要がある。
│ │ [*] Try features marked as EXPERIMENTAL │ │
ツールチェインABIの設定
メニューの Target options
から、RISC-V 32向けにターゲットオプションを設定する:
│ │ Target Architecture (riscv) ---> │ │
│ │ *** Options for riscv *** │ │
│ │ () Suffix to the arch-part │ │
│ │ [*] Omit vendor part of the target tuple │ │
│ │ *** Generic target options *** │ │
│ │ [ ] Build a multilib toolchain (READ HELP!!!) │ │
│ │ [*] Attempt to combine libraries into a single directory │ │
│ │ [*] Use the MMU │ │
│ │ Bitness: (32-bit) ---> │ │
│ │ *** Target optimisations *** │ │
│ │ (rv32imafd) Architecture level │ │
│ │ (ilp32d) Generate code for the specific ABI │ │
glibc のパス
これを書いている時点のglibcの最新版 2.13 では、まだRISC-V 32bitはサポートされていない。というわけで、今回はbuildrootが用意しているパッチ済のglibcをcrosstool-ngでも使用することにした。
│ │ C library (glibc) ---> │ │
│ │ *** Options for glibc *** │ │
│ │ Show glibc versions from (GNU) ---> │ │
│ │ Source of glibc (Custom location) ---> │ │
│ │ Custom location │ │
│ │ (/home/oku/repos//buildroot/output/build/glibc-2.32.9000-69-gbd39│ │
Source of glibc
に Custom Location を選び、 buildroot
の build
ディレクトリ以下に配置されているglibcを指定する。
何も設定していないと、以下のようなエラーで失敗してしまう。
[CFG ] mips nios2 powerpc riscv glibc does not yet support 32-bit systems
[ERROR]
[ERROR] >>
[ERROR] >> Build failed in step 'Building for multilib 1/1: '''
[ERROR] >> called in step 'Installing C library headers & start files'
[ERROR] >> called in step '(top-level)'
[ERROR] >>
[ERROR] >> Error happened in: CT_DoExecLog[scripts/functions@376]
[ERROR] >> called from: glibc_backend_once[scripts/build/libc/glibc.sh@265]
[ERROR] >> called from: CT_IterateMultilibs[scripts/functions@1608]
[ERROR] >> called from: glibc_backend[scripts/build/libc/glibc.sh@74]
[ERROR] >> called from: glibc_start_files[scripts/build/libc/glibc.sh@38]
[ERROR] >> called from: do_libc_start_files[scripts/build/libc.sh@28]
[ERROR] >> called from: main[scripts/crosstool-NG.sh@695]
[ERROR] >>
tuple-alias と Canadian-cross ビルド
crosstool-ng は、 riscv32-linux-gnu
というプレフィックスでツールチェーンをビルドしようとするが、buildrootのツールチェーンは riscv32-linux
という名前なので、これをtuple-aliasに設定する必要がある。
また、今回は RISC-V で動作するコンパイラを作成したいので、Toolchain build typeはCanadianとなる。Build systemは空欄のままでOKだが、Host system(コンパイラの動作するシステム)を riscv32-linux
に設定する。
それぞれ Toolchain options
から設定できる。
│ │ (riscv32-linux) Tuple's alias │ │
│ │ *** Toolchain type *** │ │
│ │ Type (Canadian) ---> │ │
│ │ *** Build system *** │ │
│ │ () Tuple (READ HELP!) │ │
│ │ () Tools prefix (READ HELP!) │ │
│ │ () Tools suffix (READ HELP!) │ │
│ │ *** Host system *** │ │
│ │ (riscv32-linux) Tuple (READ HELP!) │ │
│ │ () Tools prefix (READ HELP!) │ │
│ │ () Tools suffix (READ HELP!) │ │
PATHの設定とビルド
コンパイラをビルドするためのコンパイラとしては、先程buildrootで作成されたSDKをそのまま利用することになる。このため、buildrootが作成したツールチェーンをPATHに入れておく必要がある。
これは、buildrootの output/host/bin
ディレクトリが相当する。
export PATH=/home/oku/repos/buildroot/output/host/bin:$PATH
ツールチェーンをビルドには、crosstool-ngのディレクトリで
./ct-ng build
のようにする。デフォルトでは、ビルドされたツールチェーンは、 ~/x-tools/HOST-riscv32-linux/riscv32-linux-gnu
以下にインストールされるので、これを仮想マシン側に持っていくことになる。
qemuでの実行
ひとまず、以下のようなディレクトリ構成で実行する場合、
パス | 内容 |
---|---|
/home/oku/riscv32-root/rootfs |
buildrootが作ったrootfs( https://github.com/okuoku/riscv32-rootfs-proto ) |
/home/oku/riscv32-root/sdk |
crosstool-ngが作ったsdk( https://github.com/okuoku/riscv32-sdk-proto ) |
qemu-system-riscv32 \
-M virt \
-bios ~/repos/buildroot/output/images/fw_jump.bin \
-kernel ~/repos/buildroot/output/images/Image \
-append "root=/dev/root rootfstype=9p ro" \
-fsdev local,id=rootfs,path=/home/oku/riscv32-root/rootfs,security_model=none \
-device virtio-9p-pci,fsdev=rootfs,mount_tag=/dev/root \
-fsdev local,id=sdk,path=/home/oku/riscv32-root/sdk,security_model=none \
-device virtio-9p-pci,fsdev=sdk,mount_tag=sdk \
-nographic
手元の環境では -virtfs
による一括指定はうまくいかなかったので、 -fsdev
と -device
をファイルシステム毎に設定している。
起動時にはrootしかマウントされないため、手動で sdk をマウントする必要がある:
# mount -t 9p sdk /sdk
その後は、適当にコンパイル & 実行できる。
# /sdk/bin/riscv32-linux-cc test.c
# ./a.out
Hello.
かんそう
... ここまでは超どうでも良くて、ゆくゆくは、これを使うことでLinuxアプリをわざわざWASMに移植しなくても 直接GitHubにバイナリを置いて実行できる ようにしたい。例えば、GitHub上のCMakeプロジェクトを直接ビルド & 実行する環境が作れるんじゃないかなと。(ファイルの列挙にはGitHubのAPIキーが要るけど)
ひとまず、Linux環境をVirtIO + 9Pfsから直接起動することはできた。TinyEMUには9pfs ←→ HTTPのブリッジが含まれているので、それを改造してGitHubのリポジトリを直接読めるようにするのが次の一手になる。