riscV 32Bit/64Bit
Table of Contents
on OpenBSD
OpenBSDにriscv系は有るか?
riscv32-esp-elf-binutils-2.35.1.2020.1223p0.tgz 10-Apr-2025 19:38 27.5M riscv32-esp-elf-gcc-8.4.0.2021.2p3.tgz 10-Apr-2025 19:38 45.5M riscv32-esp-elf-gcc-bootstrap-8.4.0.2021.2.tgz 10-Apr-2025 19:38 18.2M riscv32-esp-elf-gdb-2.35.1.2021.2.tgz 10-Apr-2025 19:38 6.0M riscv32-esp-elf-newlib-2021.2p0.tgz 10-Apr-2025 19:38 25.6M
入れたら、こんな所に鎮座した。i386系と喧嘩しない様に、独立な陣地になってる。 riscv系も有ったけど(64?)、gdbが用意されていなかったので(32)です。組み込みを ターゲットにしてるのかな。
ob$ cd /usr/local/riscv32-esp-elf/bin/ ob$ ls riscv32-esp-elf-addr2line* riscv32-esp-elf-gcov-dump* riscv32-esp-elf-ar* riscv32-esp-elf-gcov-tool* riscv32-esp-elf-as* riscv32-esp-elf-gdb* riscv32-esp-elf-c++* riscv32-esp-elf-gdb-add-index* riscv32-esp-elf-c++filt* riscv32-esp-elf-gprof* riscv32-esp-elf-cc@ riscv32-esp-elf-ld* riscv32-esp-elf-cpp* riscv32-esp-elf-ld.bfd* riscv32-esp-elf-elfedit* riscv32-esp-elf-nm* riscv32-esp-elf-g++* riscv32-esp-elf-objcopy* riscv32-esp-elf-gcc* riscv32-esp-elf-objdump* riscv32-esp-elf-gcc-8.4.0* riscv32-esp-elf-ranlib* riscv32-esp-elf-gcc-ar* riscv32-esp-elf-readelf* riscv32-esp-elf-gcc-nm* riscv32-esp-elf-size* riscv32-esp-elf-gcc-ranlib* riscv32-esp-elf-strings* riscv32-esp-elf-gcov* riscv32-esp-elf-strip*
例の儀式を敢行。
ob$ riscv32-esp-elf-gcc hello.c /usr/local/riscv32-esp-elf/bin/riscv32-esp-elf-ld: /usr/local/riscv32-esp-elf/lib/gcc/riscv32-esp-elf/8.4.0/../../../../riscv32-esp-elf/lib/rv32imc/ilp32/crt0.o: in function `.L0 ': /pobj/riscv32-esp-elf-newlib-2021.2/newlib-esp32-esp-2021r2/libgloss/riscv/crt0.S:31: undefined reference to `__libc_fini_array' /usr/local/riscv32-esp-elf/bin/riscv32-esp-elf-ld: /pobj/riscv32-esp-elf-newlib-2021.2/newlib-esp32-esp-2021r2/libgloss/riscv/crt0.S:34: undefined reference to `__libc_init_array' collect2: error: ld returned 1 exit status
すんなり行かないのは常識です。リナとかが目標なのでしょうか。裸のマシンを想定 して、ライブラリィー無しでコンパイル。
ob$ riscv32-esp-elf-gcc -nostdlib hello.c /usr/local/riscv32-esp-elf/bin/riscv32-esp-elf-ld: warning: cannot find entry symbol _start; defaulting to 0000000000010054 ob$ file a.out a.out: ELF 32-bit LSB executable, RISC-V, version 1 MathCoPro/FPU/MAU Required
文句を言われつつコンパイル成功。コプロ搭載マシンを想定してるのかな。
//#include <stdio.h> int main() { // printf("Hello world\n"); int a = 2828; int b = 4649; int c = a + b; }
念の為、コンパイルしたのは、ハロワにも劣る奴です。逆アセして純生を味わってみます。 本当の純生は高いので無料版です。
ob$ riscv32-esp-elf-objdump -d a.out 00010054 <main>: 10054: 1101 addi sp,sp,-32 10056: ce22 sw s0,28(sp) 10058: 1000 addi s0,sp,32 1005a: 6785 lui a5,0x1 1005c: b0c78793 addi a5,a5,-1268 # b0c <main-0xf548> 10060: fef42623 sw a5,-20(s0) 10064: 6785 lui a5,0x1 10066: 22978793 addi a5,a5,553 # 1229 <main-0xee2b> 1006a: fef42423 sw a5,-24(s0) 1006e: fec42703 lw a4,-20(s0) 10072: fe842783 lw a5,-24(s0) 10076: 97ba add a5,a5,a4 10078: fef42223 sw a5,-28(s0) 1007c: 4781 li a5,0 1007e: 853e mv a0,a5 10080: 4472 lw s0,28(sp) 10082: 6105 addi sp,sp,32 10084: 8082 ret
RISC-V Instruction Set Specifications
これを見ながら解読すればいいんだな。演算はレジスター間でしか実行できない。 add a5,a5,a4 ってのは、a5 = a5 + a4 を意味してる。 それから、定数をレジスターに入れる場合、大きな定数は一度に設定できない。 なぜなら定数も命令の一部になるんだけど、その幅は制限されてるから。 最初に定数の上部をロード。次に下部を足し込むって事になる。それの命令生成は コンパイラーが勝手にやってくれる。
同じ事をリナでもやってみる。今回は定数の範囲を意識してのソースです。
int main(){ int a = 2047; int b = 2048; int c = a + b; }
コンパイル
sakae@lu:tmp$ riscv64-unknown-elf-gcc -nostdlib hello.c /usr/lib/gcc/riscv64-unknown-elf/13.2.0/../../../riscv64-unknown-elf/bin/ld: warning: cannot find entry symbol _start; defaulting to 00000000000100b0
riscv64-unknown-elf-objdump
00000000000100b0 <main>: 100b0: 1101 addi sp,sp,-32 100b2: ec22 sd s0,24(sp) 100b4: 1000 addi s0,sp,32 100b6: 7ff00793 li a5,2047 100ba: fef42623 sw a5,-20(s0) 100be: 6785 lui a5,0x1 100c0: 80078793 addi a5,a5,-2048 # 800 <main-0xf8b0> 100c4: fef42423 sw a5,-24(s0) 100c8: fec42783 lw a5,-20(s0) 100cc: 873e mv a4,a5 100ce: fe842783 lw a5,-24(s0) 100d2: 9fb9 addw a5,a5,a4 100d4: fef42223 sw a5,-28(s0) 100d8: 4781 li a5,0 100da: 853e mv a0,a5 100dc: 6462 ld s0,24(sp) 100de: 6105 addi sp,sp,32 100e0: 8082 ret
llvm-objdump こちらは汎用版です。命令がリトルエンディアン表記。即値はHex表示 になってて、通っぽい。
00000000000100b0 <main>: 100b0: 01 11 addi sp, sp, -0x20 100b2: 22 ec sd s0, 0x18(sp) 100b4: 00 10 addi s0, sp, 0x20 100b6: 93 07 f0 7f li a5, 0x7ff 100ba: 23 26 f4 fe sw a5, -0x14(s0) 100be: 85 67 lui a5, 0x1 100c0: 93 87 07 80 addi a5, a5, -0x800 100c4: 23 24 f4 fe sw a5, -0x18(s0) 100c8: 83 27 c4 fe lw a5, -0x14(s0) 100cc: 3e 87 mv a4, a5 100ce: 83 27 84 fe lw a5, -0x18(s0) 100d2: b9 9f addw a5, a5, a4 100d4: 23 22 f4 fe sw a5, -0x1c(s0) 100d8: 81 47 li a5, 0x0 100da: 3e 85 mv a0, a5 100dc: 62 64 ld s0, 0x18(sp) 100de: 05 61 addi sp, sp, 0x20 100e0: 82 80 ret
1000 lines OS on OpenBSD
これ以前見つけて、やってみてた。OpenBSDでも動作するかな? このOSはclangで 動作する様に設定されてる。OpenBSDのそれはgcc系だ。断絶が有る。いきなり OpenBSDでトライしてもいいんだけど、手始めにリナのriscv64-unknown-elf-gccで どうかテストする。64Bit様って障壁が有るけどね。
32bit/64bit
まて、32Bit/64Bitって、どんな違いが有るの?
A: RISC-V(リスクファイブ)の勉強、いいですね! 「riscv32」と「riscv64」の違いは、主に**アーキテクチャのビット幅**とそれに関連する**命令セットやレジスタ、アドレッシング能力**にあります。以下にポイントを整理します。
—
### ✅ 1. ビット幅(ISAの基本幅)
特徴 | riscv32 | riscv64 |
---------- | ---------- | ---------------------- |
レジスタ幅 | 32ビット | 64ビット |
アドレス空間 | 最大4GB(2³²) | 非常に大きい(理論上 16EB = 2⁶⁴) |
整数演算 | 32ビット | 64ビット(+ 32ビットも可能) |
—
### ✅ 2. 命令セットの違い(拡張含む)
RISC-Vにはベースの命令セットと、必要に応じて加える拡張命令セットがあります。
- rv32: RV32I, RV32IM, RV32IMF, など(I = Integer, M = Multiply, F = Float)
- rv64: RV64I, RV64IM, RV64IMAFD など
#### 例:
- `add`(整数加算)はどちらにもあります。
- `ld`(64ビットロード)は rv64 のみ。
- `lw`(32ビットロード)はどちらにもあるが、rv64では符号拡張して64ビットレジスタに格納される。
—
### ✅ 3. ABI(Application Binary Interface)の違い
項目 | riscv32 | riscv64 |
--------------- | --------------------------- | --------------------------------- |
ABI名 | `ilp32`, `ilp32f`, `ilp32d` | `lp64`, `lp64f`, `lp64d` |
データ型サイズ | int/long/pointer = 32bit | long/pointer = 64bit, int = 32bit |
関数呼び出し規約、構造体の配置 | 異なる | 異なる |
—
### ✅ 4. 実用面での違い
項目 | riscv32 | riscv64 |
------------- | ----------- | ------------------------ |
用途 | 組み込み機器、マイコン | サーバ、デスクトップ、汎用OS |
Linuxカーネルサポート | あり(制限あり) | フルサポート(Debian, Fedoraなど) |
性能・メモリ対応 | 小規模向け | 大規模対応可能 |
—
### ✅ 5. 実機・エミュレータでの違い(QEMUなど)
- QEMUでは `riscv32` と `riscv64` それぞれの仮想マシンを起動可能です。
- `qemu-system-riscv32`
- `qemu-system-riscv64`
それぞれ別のLinuxディストリビューション用イメージやクロスコンパイラを使う必要があります。
—
### ✅ まとめ
比較項目 | riscv32 | riscv64 |
------------ | --------- | --------------- |
アドレス可能なメモリ空間 | 小さい(~4GB) | 大きい(64bit空間) |
レジスタのビット幅 | 32bit | 64bit |
対象アプリケーション | 組み込み、IoT | 汎用計算、Linux上のアプリ |
ABI・命令セット | rv32系 | rv64系 |
—
32/64で命令に違いがあるとな。そして、同じビットのマシンでも、乗算とか 浮動小数点機構はオプション扱いになってるとな。
firmware
100 lines OSでは、ファームに
opensbi-riscv32-generic-fw_dynamic.bin
なんてのを利用するんでDLしとけって
指示されてる。これって、昔言葉で言う、BIOSの事なのかな。DL元はqemuのサイトだ。
オイラーはqemuならgitから一式DLしてるぞ。
qemu/pc-bios/README
- OpenSBI (https://github.com/riscv/opensbi) aims to provide an open-source reference implementation of the RISC-V Supervisor Binary Interface (SBI) specifications for platform-specific firmwares executing in M-mode. For all supported platforms, OpenSBI provides several runtime firmware examples. These example firmwares can be used to replace the legacy riscv-pk bootloader and enable the use of well-known bootloaders such as U-Boot. OpenSBI is distributed under the terms of the BSD 2-clause license ("Simplified BSD License" or "FreeBSD License", SPDX: BSD-2-Clause). OpenSBI source code also contains code reused from other projects described here: https://github.com/riscv/opensbi/blob/master/ThirdPartyNotices.md.
ファームは唯のデータだと言うので、人様がそれらしく読めるのを抽出する。
sakae@lu:pc-bios$ strings opensbi-riscv32-generic-fw_dynamic.bin : v1.5.1 OpenSBI %s ____ _____ ____ _____ / __ \ / ____| _ \_ _| | | | |_ __ ___ _ __ | (___ | |_) || | | | | | '_ \ / _ \ '_ \ \___ \| _ < | | | |__| | |_) | __/ | | |____) | |_) || |_ \____/| .__/ \___|_| |_|_____/|____/_____| | | |_| : Platform Name : %s Platform Features : %s Platform HART Count : %u Platform IPI Device : %s Platform Timer Device : %s @ %luHz
1000 lines OS の最初の一歩の結果。
OpenSBI v1.2 : Platform Name : riscv-virtio,qemu Platform Features : medeleg Platform HART Count : 1 Platform IPI Device : aclint-mswi Platform Timer Device : aclint-mtimer @ 10000000Hz
手持ちのファームの方が新らしいな。
Ubuntu on 64Bit
見様見真似でコードを作成したけど、下記の様に致命的なエラーになった。 dummyを宣言してるのは、bssエリアが存在する様にするため。
sakae@lu:my$ sh run.sh + QEMU=qemu-system-riscv64 + CC=riscv64-unknown-elf-gcc + CFLAGS=-Wall -Wextra -fno-stack-protector -ffreestanding -nostdlib + riscv64-unknown-elf-gcc -Wall -Wextra -fno-stack-protector -ffreestanding -nostdlib -Wl,-Tkernel.ld -Wl,-Map=kernel.map -o kernel.elf kernel.c kernel.c:8:12: warning: 'dummy' defined but not used [-Wunused-variable] 8 | static int dummy[100]; // dummy for bss | ^~~~~ /usr/lib/gcc/riscv64-unknown-elf/13.2.0/../../../riscv64-unknown-elf/bin/ld: warning: kernel.elf has a LOAD segment with RWX permissions /tmp/ccMaxAWy.o: in function `kernel_main': kernel.c:(.text+0x58): relocation truncated to fit: R_RISCV_HI20 against symbol `__bss_end' defined in .bss section in kernel.elf collect2: error: ld returned 1 exit status
とうとう、あの人に相談。
A: RISC-V はアドレスを24ビット即値(HI20 + LO12)で組み立てます。問題は、GCCがアドレス演算をするたびにそれを命令列でエミュレートするため、範囲外だとリンクエラーになるということです。
__bss_end
のような アドレス定数を直接使用した演算を C 側で行うと、GCC はそのアドレスに対し HI20/LO12 などのリロケーションを生成します。
ところが、リンク時点で .bss が .text から遠い場合(たとえば約 0x200 バイト以上離れている場合)、HI20 に収まる範囲を超えるとエラーになります。これはRISC-V 固有の制限です(即値オフセット範囲: ±1MiB)。
それを解消するには、C言語内での演算を止めてアセンブラにするしかないです。
sakae@lu:my$ cat bss.S .section .text .global zero_bss zero_bss: la t0, __bss la t1, __bss_end 1: beq t0, t1, 2f sb zero, 0(t0) addi t0, t0, 1 j 1b 2: ret .section .rodata zero: .byte 0
sakae@lu:my$ cat kernel.c #include <stdint.h> #include <stddef.h> void zero_bss(void); extern uint8_t __bss[]; extern uint8_t __bss_end[]; extern uint8_t __stack_top[]; //static int dummy[100]; // dummy for bss /* void *memset(void *s, int c, size_t n) { */ /* uint8_t *p = s; */ /* while (n--) { */ /* *p++ = (uint8_t)c; */ /* } */ /* return s; */ /* } */ void kernel_main(void) { // memset(__bss, 0, (uintptr_t)__bss_end - (uintptr_t)__bss); zero_bss(); while (1) { } } __attribute__((section(".text.boot"))) void _start(void) { asm volatile ("la sp, __stack_top"); kernel_main(); while (1) { asm volatile ("wfi"); } }
sakae@lu:my$ cat kernel.ld ENTRY(_start) SECTIONS { /* OpenSBIがfw_dynamicとして使用される場合、payloadは0x80200000から開始 */ . = 0x80200000; .text.boot : { *(.text.boot) } .text : { *(.text*) } .rodata : { *(.rodata*) } .data : { *(.data*) } .bss : { __bss = .; *(.bss*) *(COMMON) __bss_end = .; } . = ALIGN(16); __stack_top = .; }
sakae@lu:my$ cat run.sh #!/bin/bash set -xue QEMU=qemu-system-riscv64 CC=riscv64-unknown-elf-gcc riscv64-unknown-elf-as bss.S -o bss.o CFLAGS="-Wall -Wextra -fno-stack-protector -ffreestanding -nostdlib" $CC $CFLAGS -Wl,-Tkernel.ld -Wl,-Map=kernel.map -o kernel.elf \ kernel.c bss.o $QEMU -machine virt -bios default -nographic -serial mon:stdio --no-reboot \ -kernel kernel.elf
sakae@lu:my$ sh run.sh OpenSBI v1.5.1 Platform Name : riscv-virtio,qemu Platform Features : medeleg Platform HART Count : 1 Platform IPI Device : aclint-mswi Platform Timer Device : aclint-mtimer @ 10000000Hz Platform Console Device : uart8250 Platform HSM Device : --- Platform PMU Device : --- Platform Reboot Device : sifive_test Platform Shutdown Device : sifive_test Firmware Base : 0x80000000 Firmware Size : 212 KB Runtime SBI Version : 1.0 Domain0 Name : root Domain0 Boot HART : 0 Domain0 HARTs : 0* Domain0 Region00 : 0x0000000002000000-0x000000000200ffff (I) Domain0 Region01 : 0x0000000080000000-0x000000008003ffff () Domain0 Region02 : 0x0000000000000000-0xffffffffffffffff (R,W,X) Domain0 Next Address : 0x0000000080200000 Domain0 Next Arg1 : 0x0000000087e00000 Domain0 Next Mode : S-mode Domain0 SysReset : yes Boot HART ID : 0 Boot HART Domain : root Boot HART Priv Version : v1.12 Boot HART Base ISA : rv64imafdch Boot HART ISA Extensions : time,sstc Boot HART PMP Count : 16 Boot HART PMP Granularity : 4 Boot HART PMP Address Bits: 54 Boot HART MHPM Count : 16 Boot HART MIDELEG : 0x0000000000001666 Boot HART MEDELEG : 0x0000000000f0b509
32Bitの時は、Boot HART PMP Address Bits: 32 になってた。
OpenBSD on 32Bit
あたり前だけど、オリジナルのmemsetで動いた。
(gdb) bt #0 memset (s=0x80200094 <dummy>, c=0, n=400) at kernel.c:13 #1 0x80200090 in kernel_main () at kernel.c:21 #2 0x80200012 in _start () at kernel.c:29 Backtrace stopped: previous frame inner to this frame (corrupt stack?) (gdb) disassemble Dump of assembler code for function memset: 0x80200018 <+0>: addi sp,sp,-48 0x8020001a <+2>: sw s0,44(sp) 0x8020001c <+4>: addi s0,sp,48 0x8020001e <+6>: sw a0,-36(s0) 0x80200022 <+10>: sw a1,-40(s0) 0x80200026 <+14>: sw a2,-44(s0) => 0x8020002a <+18>: lw a5,-36(s0) 0x8020002e <+22>: sw a5,-20(s0) 0x80200032 <+26>: j 0x8020004c <memset+52> 0x80200034 <+28>: lw a5,-20(s0) 0x80200038 <+32>: addi a4,a5,1 0x8020003c <+36>: sw a4,-20(s0) 0x80200040 <+40>: lw a4,-40(s0) 0x80200044 <+44>: andi a4,a4,255 0x80200048 <+48>: sb a4,0(a5) 0x8020004c <+52>: lw a5,-44(s0) 0x80200050 <+56>: addi a4,a5,-1 0x80200054 <+60>: sw a4,-44(s0) 0x80200058 <+64>: bnez a5,0x80200034 <memset+28> 0x8020005a <+66>: lw a5,-36(s0) 0x8020005e <+70>: mv a0,a5 0x80200060 <+72>: lw s0,44(sp) 0x80200062 <+74>: addi sp,sp,48 0x80200064 <+76>: ret
C語だと、効率悪いな。
ob$ riscv32-esp-elf-nm kernel.elf 80200094 B __bss 80200224 B __bss_end 80200230 B __stack_top 80200000 T _start 80200094 b dummy 80200066 T kernel_main 80200018 T memset
リンカーが出力するmapより、こちらの方が慣れているせいか見易いな。
OpenBSD amd64なマシンにもriscv32の環境を作ってみた。試運転です。
0x80200024 in zero_bss () (gdb) disassemble Dump of assembler code for function zero_bss: => 0x80200024 <+0>: auipc t0,0x0 0x80200028 <+4>: addi t0,t0,31 # 0x80200043 0x8020002c <+8>: auipc t1,0x0 0x80200030 <+12>: addi t1,t1,23 # 0x80200043 0x80200034 <+16>: beq t0,t1,0x80200040 <zero_bss+28> 0x80200038 <+20>: sb zero,0(t0) 0x8020003c <+24>: addi t0,t0,1 0x8020003e <+26>: j 0x80200034 <zero_bss+16> 0x80200040 <+28>: ret (gdb) si 0x80200028 in zero_bss () (gdb) si 0x8020002c in zero_bss () (gdb) display/i $pc 1: x/i $pc => 0x8020002c <zero_bss+8>: auipc t1,0x0 (gdb) si 0x80200030 in zero_bss () 1: x/i $pc => 0x80200030 <zero_bss+12>: addi t1,t1,23
ステップ実行はPC(program counter)を指定してどうぞ。何かPDP-11 みたいで 懐かしい。インテルさんは、IPと言うんだっけな。馴染めない名前だ。
1000 Lines OS by Rust
Writing an OS in 1,000 Lines を Rust でやってみた
猛者さんである。素直に尊敬しますよ。
md2html
mdファイルは見るのが大変。htmlに変換してw3mで閲覧したい。既製品を 探してみたがFitしない。
md2html by go こちらからソースを頂き、サクっと作った。
mkdir my cp main.go my cd my go mod init md2html go mod tidy go build
使い方。伝統のCLIである。安心するぞ。
for f in *.md do t=`echo $f | sed 's/md$/html/'` md2html $f > $t done
README
年寄を題材にした本である。60代からアラハン(100)男女7人が登場。 著者は仙台の方。そのせいか読んでいて土地勘が有るぞ。昔10年ぐらい住んで いましたから。
膝に水が溜まる痛いと病院通い。そこで紹介されたのが、ノルディック・ウォーキング。 スキーのストック2本を補助にして楽々歩きましょ。何でも体への負担が30%も軽減 され、かつ体全体の90%もの筋肉を使うため非常に良い運動になるそうな。 無理してスポーツ・ジムの会員になって水泳なんてやる必要は全く無いそうな。 ああ、仙台在住の時は、キリン・スポーツクラブなんてのに入会してたな。 今でも有るのかしらん。
で、2本の杖で練習してた66歳のオバサンが同好の氏を見付けた。今度一緒に 温泉療法に行きましょ。何処がいいかしら? 有名な鳴子温泉なんてどう? 懐しいねぇ。あそこは秋が最高なんだよなー。御当地案内っぽいな。 川崎町には、日本でも数少ない、公認ノルディック・ウォーキングコースが有る そうな。
この他に、墓終いはどうするだとか、遺影はどうするだとかの、年寄り向けな 話題が取り上げられている。誰もが避けて通れないけど、何となく話題にしたくない 話題。これを読んで、そろそろ考えてみよーって、気になったぞ。
そして、もう一冊。 80歳、歩いて日本縦断 表紙にもある様に、この方はノルディック・ウォーキングの実践者。心筋梗塞 で倒れ、手術を受けて復帰。旅の途中でも中断し、主治医の診断を受けての再開を 何度も繰り返したそうな。荷物は身の周り品だけ。10kg。ITはからっきし駄目 との事で、ラクラクホンの使い方を旅の途中でマスターしたとか。人それぞれである。
人それぞれと言うなら、オイラーはスマホなんて、現代の寄生虫だと思うぞ。 スマホが出現した当時は、まあ良かった。次第に脳を侵食した。中毒症状を発症。 宿主の時間と金とプライバシィーを奪って、せっせと企業に貢ぐ奴ですから! お情けで、多少の快楽を与えてくれるけど。秤にかけたら、絶対に前者の方が 得をしてると思うぞ(個人の感触です)。