FPGA開発日記

FPGAというより、コンピュータアーキテクチャかもね! カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages

LLVMのバックエンドを作るための第一歩 (6. ELFとリロケーションレコード)

f:id:msyksphinz:20190425001356p:plain
LLVM Compiler Infrastructure

LLVMのバックエンドにオリジナルターゲットアーキテクチャを追加していくプロジェクト、MYRISCVXターゲットアーキテクチャを追加したら、今度はELFの情報を追加する必要がある。

MYRISCVXはRISC-Vのオリジナル実装なんで、ELFやABIはRISC-Vと全く同一で進めようと思う。

MYRISCVXのELF出力を登録する

  • include/llvm/BinaryFormat/ELF.h こちらもELFを生成するにあたり必要だ。ELFのアーキテクチャ番号は、実は取り決めがありhttp://www.sco.com/developers/gabi/latest/ch4.eheader.htmlなどを参考にすること。このページでは、EM_RISCVは243と決められている。ここでは、まだ誰も使用していない248をMYRISCVXとして使用することにする。
  diff --git a/include/llvm/BinaryFormat/ELF.h b/include/llvm/BinaryFormat/ELF.h
  index ce35d127d43..55a6c7ddb4b 100644
  --- a/include/llvm/BinaryFormat/ELF.h
  +++ b/include/llvm/BinaryFormat/ELF.h
  @@ -312,6 +312,7 @@ enum {
     EM_RISCV = 243,         // RISC-V
     EM_LANAI = 244,         // Lanai 32-bit processor
     EM_BPF = 247,           // Linux kernel bpf virtual machine
  +  EM_MYRISCVX = 248,      // MYRISCVX
   };
  
   // Object file classes.

さらに、MYRISCVX向けのe_flagsを作成する。e_flagsはプロセッサ固有のフラグを示すのに使われる。たとえば、アーキテクチャ内の固有の実装を示したり、ISAの中のどのカテゴリをサポートしているか、などを示すためにe_flagsが使用される。例えば、MIPSの例を示す。

  • include/llvm/BinaryFormat/ELF.h
  // Mips Specific e_flags
  enum : unsigned {
    EF_MIPS_NOREORDER = 0x00000001, // Don't reorder instructions
    EF_MIPS_PIC = 0x00000002,       // Position independent code
    EF_MIPS_CPIC = 0x00000004,      // Call object with Position independent code
    EF_MIPS_ABI2 = 0x00000020,      // File uses N32 ABI
    EF_MIPS_32BITMODE = 0x00000100, // Code compiled for a 64-bit machine
  ...

RISC-Vの場合はどうなっているのか?以下の資料を参考にすると、以下の項目を定義しなければならないようだ。

Bit 0 Bit 1 - 2 Bit 3 Bit 4 Bit 5 - 31
RVC Float ABI RVE TSO Reserved

詳細はリンク先に譲る。ここでは、ELF.hに以下を追加した(というか、本家のRISC-Vと一緒だ)。

  enum : unsigned {
    EF_RISCV_RVC = 0x0001,
    EF_RISCV_FLOAT_ABI = 0x0006,
    EF_RISCV_FLOAT_ABI_SOFT = 0x0000,
    EF_RISCV_FLOAT_ABI_SINGLE = 0x0002,
    EF_RISCV_FLOAT_ABI_DOUBLE = 0x0004,
    EF_RISCV_FLOAT_ABI_QUAD = 0x0006,
    EF_RISCV_RVE = 0x0008
  };

リロケーションレコード

関数呼び出しなどをコンパイルするときに、その関数が実際にどこに配置されるかが分からないため、再配置(リロケーション)のためのシンボルが挿入される。 このリロケーションのアルゴリズムアーキテクチャ毎に決められており、LLVMではリンク時にリロケーションのアルゴリズムに応じてアドレス計算の命令が挿入される。 ELFフォーマットには、リロケーションの情報も含まれており、MYRISCVXもリロケーション情報を登録しなければならない。 ここではRISC-Vと全く同様のリロケーション情報を登録した。 RISC-Vのリロケーションは、https://github.com/riscv/riscv-elf-psabi-doc/blob/master/riscv-elf.md#relocations で見ることができる。 このリロケーション情報をinclude/llvm/BinaryFormat/ELFRelocs/MYRISCVX.defに置く。

  • include/llvm/BinaryFormat/ELFRelocs/MYRISCVX.def
  #ifndef ELF_RELOC
  #error "ELF_RELOC must be defined"
  #endif
  
  ELF_RELOC(R_MYRISCVX_NONE,               0)
  ELF_RELOC(R_MYRISCVX_32,                 1)
  ELF_RELOC(R_MYRISCVX_64,                 2)
  ELF_RELOC(R_MYRISCVX_RELATIVE,           3)
  ELF_RELOC(R_MYRISCVX_COPY,               4)
  ELF_RELOC(R_MYRISCVX_JUMP_SLOT,          5)
  ELF_RELOC(R_MYRISCVX_TLS_DTPMOD32,       6)
  ELF_RELOC(R_MYRISCVX_TLS_DTPMOD64,       7)
  ELF_RELOC(R_MYRISCVX_TLS_DTPREL32,       8)
  ELF_RELOC(R_MYRISCVX_TLS_DTPREL64,       9)
  ELF_RELOC(R_MYRISCVX_TLS_TPREL32,       10)
  ELF_RELOC(R_MYRISCVX_TLS_TPREL64,       11)
  ELF_RELOC(R_MYRISCVX_BRANCH,            16)
  ELF_RELOC(R_MYRISCVX_JAL,               17)
  ELF_RELOC(R_MYRISCVX_CALL,              18)
  ELF_RELOC(R_MYRISCVX_CALL_PLT,          19)
  ELF_RELOC(R_MYRISCVX_GOT_HI20,          20)
  ELF_RELOC(R_MYRISCVX_TLS_GOT_HI20,      21)
  ELF_RELOC(R_MYRISCVX_TLS_GD_HI20,       22)
  ELF_RELOC(R_MYRISCVX_PCREL_HI20,        23)
  ELF_RELOC(R_MYRISCVX_PCREL_LO12_I,      24)
  ELF_RELOC(R_MYRISCVX_PCREL_LO12_S,      25)
  ELF_RELOC(R_MYRISCVX_HI20,              26)
  ELF_RELOC(R_MYRISCVX_LO12_I,            27)
  ELF_RELOC(R_MYRISCVX_LO12_S,            28)
  ELF_RELOC(R_MYRISCVX_TPREL_HI20,        29)
  ELF_RELOC(R_MYRISCVX_TPREL_LO12_I,      30)
  ELF_RELOC(R_MYRISCVX_TPREL_LO12_S,      31)
  ELF_RELOC(R_MYRISCVX_TPREL_ADD,         32)
  ELF_RELOC(R_MYRISCVX_ADD8,              33)
  ELF_RELOC(R_MYRISCVX_ADD16,             34)
  ELF_RELOC(R_MYRISCVX_ADD32,             35)
  ELF_RELOC(R_MYRISCVX_ADD64,             36)
  ELF_RELOC(R_MYRISCVX_SUB8,              37)
  ELF_RELOC(R_MYRISCVX_SUB16,             38)
  ELF_RELOC(R_MYRISCVX_SUB32,             39)
  ELF_RELOC(R_MYRISCVX_SUB64,             40)
  ELF_RELOC(R_MYRISCVX_GNU_VTINHERIT,     41)
  ELF_RELOC(R_MYRISCVX_GNU_VTENTRY,       42)
  ELF_RELOC(R_MYRISCVX_ALIGN,             43)
  ELF_RELOC(R_MYRISCVX_RVC_BRANCH,        44)
  ELF_RELOC(R_MYRISCVX_RVC_JUMP,          45)
  ELF_RELOC(R_MYRISCVX_RVC_LUI,           46)
  ELF_RELOC(R_MYRISCVX_GPREL_I,           47)
  ELF_RELOC(R_MYRISCVX_GPREL_S,           48)
  ELF_RELOC(R_MYRISCVX_TPREL_I,           49)
  ELF_RELOC(R_MYRISCVX_TPREL_S,           50)
  ELF_RELOC(R_MYRISCVX_RELAX,             51)
  ELF_RELOC(R_MYRISCVX_SUB6,              52)
  ELF_RELOC(R_MYRISCVX_SET6,              53)
  ELF_RELOC(R_MYRISCVX_SET8,              54)
  ELF_RELOC(R_MYRISCVX_SET16,             55)
  ELF_RELOC(R_MYRISCVX_SET32,             56)
  ELF_RELOC(R_MYRISCVX_32_PCREL,          57)

このMYRISCVXのリロケーションテーブルは、e_flagと同様、include/llvm/BinaryFormat/ELF.h上でインクルードされる。

  • include/llvm/BinaryFormat/ELF.h
    // MYRISCVX Specific e_flags
    enum : unsigned {
      EF_MYRISCVX_RVC = 0x0001,
      EF_MYRISCVX_FLOAT_ABI = 0x0006,
    ...
        
    // ELF Relocation types for MYRISCXV
    enum {
    #include "ELFRelocs/MYRISCVX.def"
    };

このリロケーション情報を使ってどのようにしてアドレス計算を行うのかは、おいおい解説していく。