iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 24
0

前情提要


昨天大概在抽象的層面理解連結這個動作的必要性和最後可以產出的結果,今天就來讓我們一窺 RISC-V 如何提供連結的選項。

RISC-V 的連結選項


我們可以重新回顧昨日展示的物件檔內容

$ riscv64-unknown-linux-gnu-objdump -dr ~/main.o

/root/main.o:     file format elf64-littleriscv


Disassembly of section .text:

0000000000000000 <main>:
   0:   1141                    addi    sp,sp,-16
                        0: R_RISCV_ALIGN        *ABS*
   2:   e406                    sd      ra,8(sp)
   4:   e022                    sd      s0,0(sp)
   6:   0800                    addi    s0,sp,16
   8:   4589                    li      a1,2
   a:   4505                    li      a0,1
   c:   00000097                auipc   ra,0x0
                        c: R_RISCV_CALL add
                        c: R_RISCV_RELAX        *ABS*
  10:   000080e7                jalr    ra
  14:   87aa                    mv      a5,a0
  16:   85be                    mv      a1,a5
  18:   000007b7                lui     a5,0x0
                        18: R_RISCV_HI20        .LC0
                        18: R_RISCV_RELAX       *ABS*
  1c:   00078513                mv      a0,a5
                        1c: R_RISCV_LO12_I      .LC0
                        1c: R_RISCV_RELAX       *ABS*
  20:   00000097                auipc   ra,0x0
                        20: R_RISCV_CALL        printf
                        20: R_RISCV_RELAX       *ABS*
  24:   000080e7                jalr    ra
  28:   4781                    li      a5,0
  2a:   853e                    mv      a0,a5
  2c:   60a2                    ld      ra,8(sp)
  2e:   6402                    ld      s0,0(sp)
  30:   0141                    addi    sp,sp,16
  32:   8082                    ret

其中我們可以看到數個 R_RISCV 開頭的東西,那些就是所謂的重定標記。可以使用 readelf 工具進一步觀察:

riscv64-unknown-linux-gnu-readelf -r ~/main.o

Relocation section '.rela.text' at offset 0x1b8 contains 9 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000000000  00000000002b R_RISCV_ALIGN                        0
00000000000c  000900000012 R_RISCV_CALL      0000000000000000 add + 0
00000000000c  000000000033 R_RISCV_RELAX                        0
000000000018  00060000001a R_RISCV_HI20      0000000000000000 .LC0 + 0
000000000018  000000000033 R_RISCV_RELAX                        0
00000000001c  00060000001b R_RISCV_LO12_I    0000000000000000 .LC0 + 0
00000000001c  000000000033 R_RISCV_RELAX                        0
000000000020  000a00000012 R_RISCV_CALL      0000000000000000 printf + 0
000000000020  000000000033 R_RISCV_RELAX                        0

這些內容可以在 RISC-V 基金會的官方文件中窺知一二,他們的意義分別是:

  • R_RISCV_ALIGN:代表這裡需要對齊。這出現在 main 函式的開頭,也是合情合理。
  • R_RISCV_CALL:出現兩次的呼叫,就是分別對函數 add 以及 printf。
  • R_RISCV_HI20:為了成功呼叫 printf,這裡準備了一個 %d\n 的格式字串,這個字串因為存在於全域的某個位址,因此雖然乍看之下看不出來,但其實也需要一組重定向。編譯器安排給我們的是 lui+addi 的組合。
  • R_RISCV_LO12_I:參考上述,這個是表示接下來的 12-bit 載入是什麼型態的ˇ格式字串,這個字串因為存在於全域的某個位址,因此雖然乍看之下看不出來,但其實也需要一組重定向。編譯器安排給我們的是 lui+addi 的組合。
  • R_RELAX:這個我們應該不會有機會實作到。簡單來說是把類似 auipc+jalr 或是 lui+addi 這種組合化約成單一指令的行為。

意外的發現!!!

本來筆者正要思考從 as 的強化還是 ld 的骨架切入,結果發現我們使用的 riscv-go 沒有支援這麼多重定項目!請看:

1730 // Relocation types for RISC-V processors.
1731 type R_RISCV int
1732
1733 const (
1734         R_RISCV_NONE         R_RISCV = 0  /* No relocation. */
1735         R_RISCV_32           R_RISCV = 1  /* Add 32 bit zero extended symbol value */
1736         R_RISCV_64           R_RISCV = 2  /* Add 64 bit symbol value. */
1737         R_RISCV_RELATIVE     R_RISCV = 3  /* Add load address of shared object. */
1738         R_RISCV_COPY         R_RISCV = 4  /* Copy data from shared object. */
1739         R_RISCV_JUMP_SLOT    R_RISCV = 5  /* Set GOT entry to code address. */
1740         R_RISCV_TLS_DTPMOD32 R_RISCV = 6  /* 32 bit ID of module containing symbol */
1741         R_RISCV_TLS_DTPMOD64 R_RISCV = 7  /* ID of module containing symbol */
1742         R_RISCV_TLS_DTPREL32 R_RISCV = 8  /* 32 bit relative offset in TLS block */
1743         R_RISCV_TLS_DTPREL64 R_RISCV = 9  /* Relative offset in TLS block */
1744         R_RISCV_TLS_TPREL32  R_RISCV = 10 /* 32 bit relative offset in static TLS block */
1745         R_RISCV_TLS_TPREL64  R_RISCV = 11 /* Relative offset in static TLS block */
1746 )

這是 src/debug/elf.go 檔案裡面的重定部分,看來筆者終究是一頭撞上了高牆;這個連 R_RISCV_CALL 的重定項目都沒有,該如何繼續下去?不過沒關係,讓我們化危機為轉機吧!明天,我們來補強這份文件,然後貢獻回 riscv/riscv-go 的 github 上游!各位讀者,我們明天再會!


上一篇
第二十三日:連結的概念
下一篇
第二十五日:眾裡尋 relocation type 千百度......
系列文
與妖精共舞:在 RISC-V 架構上使用 GO 語言實作 binutils 工具包30

尚未有邦友留言

立即登入留言