RISC-V アセンブリを読む その0
RISC-Vのアセンブリの個別の命令に関してはある程度理解しているのですが、高級言語で書かれたものがどのようなアセンブリに変換されるのかの理解が乏しいので、今回はC言語をRISC-V向けにコンパイルして出力されたアセンブリを眺めてみようと思う。
以下が使用したmakefile。
clang := clang-14 opt := opt objdump := riscv64-unknown-elf-objdump # include library file targeting riscv64 IFILE := ~/riscv64_github/riscv64-unknown-elf/include SRCS := test0.c OBJS := $(SRCS:.c=.o) ASMS := $(SRCS:.c=.S) all: $(ASMS) $(OBJS): $(SRCS) $(clang) -c $< -o $@ -target riscv64 -O3 -I $(IFILE) $(ASMS): $(OBJS) $(objdump) -d $< > $@ clean: rm -f $(OBJS) $(ASMS)
また以下がclang-14のバージョン。
$ clang-14 --version Ubuntu clang version 14.0.0-1ubuntu1 Target: x86_64-pc-linux-gnu Thread model: posix InstalledDir: /usr/bin
以下で、様々なC言語ソースコードから出力されるRISC-Vアセンブリを眺めていこうと思う。
下準備
まずは以下のようなHelloWorldソースコードの出力結果を調べることにする。
#include <stdio.h> int main() { printf("Hello, World"); return 0; }
test0.o: file format elf64-littleriscv Disassembly of section .text: 0000000000000000 <main>: 0: 1141 addi sp,sp,-16 2: e406 sd ra,8(sp) 4: 00000537 lui a0,0x0 8: 00050513 mv a0,a0 c: 00000097 auipc ra,0x0 10: 000080e7 jalr ra # c <main+0xc> 14: 4501 li a0,0 16: 60a2 ld ra,8(sp) 18: 0141 addi sp,sp,16 1a: 8082 ret
lui命令などで0x0と意味のない即値をしているように見えるのは、test0.oがまだオブジェクトファイルであり、リンク前であるのでどのアドレスに割り当てられるかがわかっていないからである。リロケーション情報を付与して$(obdjump) -d -r $< > $@
というコマンドを実行すると以下のようにリロケーション情報も付与されたファイルが出力される。このリロケーション情報をもとにリンカはリンクを実行する。
0000000000000000 <main>: 0: 1141 addi sp,sp,-16 2: e406 sd ra,8(sp) 4: 00000537 lui a0,0x0 4: R_RISCV_HI20 .L.str 8: 00050513 mv a0,a0 8: R_RISCV_LO12_I .L.str c: 00000097 auipc ra,0x0 c: R_RISCV_CALL printf 10: 000080e7 jalr ra # c <main+0xc> 14: 4501 li a0,0 16: 60a2 ld ra,8(sp) 18: 0141 addi sp,sp,16 1a: 8082 ret
具体的にどのアドレスに飛んだかもわかった状態でアセンブリを眺めたいと思い、elfという実行ファイルを作ってそれからobjdumpで出力されるアセンブリを眺めることにする。 作って学ぶコンピュータアーキテクチャという本を参考に以下のようにリンクさせた。
$ riscv64-unknown-elf-gcc -static -mcmodel=medany -fno-common -fno-builtin-printf -nostdlib -nostartfiles -lm -lgcc -T link.ld syscall.c crt.S test0.c -o test0
ここで、syscall.c crt.S link.ldなどはriscv-toolsというリポジトリから持ってきた。これをobjdumpしたら以下のような出力が得られた。
000000008000226c <main>: 8000226c: 1141 addi sp,sp,-16 8000226e: e406 sd ra,8(sp) 80002270: e022 sd s0,0(sp) 80002272: 0800 addi s0,sp,16 80002274: 00000517 auipc a0,0x0 80002278: 1bc50513 addi a0,a0,444 # 80002430 <main+0x1c4> 8000227c: aebff0ef jal ra,80001d66 <printf> 80002280: 4781 li a5,0 80002282: 853e mv a0,a5 80002284: 60a2 ld ra,8(sp) 80002286: 6402 ld s0,0(sp) 80002288: 0141 addi sp,sp,16 8000228a: 8082 ret
上でclangをコンパイラとして用いようとしたのに、結局gccを使ってコンパイルすることになってしまった。ただ、そこまでclangとgccで大きな違いはないはずなので、gccでコンパイルしていこうと思う。