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コンパイルしていこうと思う。

これを使って次回以降RISC-Vアセンブリを詳しく読んでいこうと思う。