iT邦幫忙

2024 iThome 鐵人賽

DAY 27
0

1. Linker 的作用

  • 輸入:Linker 的輸入是多個 object file(例如 foo.o, lib.o for RISC-V)。每個 object file 包含文本段(Text Segment)、數據段(Data Segment)、符號表、重定位表等資訊。
  • 輸出:Linker 的輸出是可執行的機器碼(例如 a.out for RISC-V)。
  • 作用:Linker 的作用是將各個 object file 組合成為一個完整的可執行檔,支持文件的分離編譯。也就是說,修改單一文件並不需要重新編譯整個程式。
    • 舉例:Linux 源碼有超過 2000 萬行代碼,連結器允許分離編譯,極大提升開發效率。

2. Linker 如何組合多個 object 模組

  • 步驟1:將每個 object file 的文本段(Text Segment)組合在一起。
  • 步驟2:將每個 object file 的數據段(Data Segment)組合起來,並接續在步驟1生成的文本段之後。
  • 步驟3:解析引用,通過重定位表(Relocation Table)來處理每個條目,並填入絕對地址。

3. 需要重定位的地址類型

  • PC-relative 地址(例如 beqbnejalauipc/addi:這類地址不需要重定位,因為這些指令使用與當前 PC 相對的地址。
  • 外部函數引用(例如 jalauipc/jalr:這些引用需要重定位,因為組譯時無法確定外部函數的地址。
  • 靜態數據引用(例如 lwswlui/addi:這些數據段在組合時會被重新安置,因此也需要重定位。

4. 哪些指令需要進行重定位編輯

  • J-Format:只在進行外部跳轉時(externally jumping)需要重定位,例如 jal 指令。
  • 使用全域指標(gp)訪問 .data 資料變數的 load/store 指令:全域指標(gp)是指向數據段和靜態段的指標。
  • 其他指令如 luiaddiauipc/jalr 當跳轉到外部時也需要重定位
  • 條件分支指令(B-Type)不需要進行重定位,因為即使文本段被重新定位,PC-relative 地址仍能保持正確。

5. 解析引用的過程

  • 在 RISC-V RV32 架構中,Linker 假設第一個文本段從地址 0x10000 開始。
  • Linker 需要知道每個文本段和數據段的長度及順序,並計算出每個標籤或數據段的絕對地址。
  • Linker 會在所有符號表中查找引用,若未找到,則會查找標準庫(例如 printf)。確定絕對地址後,Linker 會填入相應的機器碼。
  • 輸出:包含文本段、數據段和其他除錯資訊的可執行檔案。

6. 靜態鏈結與動態鏈結

  • 靜態鏈結:所有程式所依賴的函式庫會被嵌入到可執行檔內。如果函式庫更新,則需要重新編譯使用該函式庫的程式。
    • 優點:可執行檔是自包含的,不需要依賴外部庫。
  • 動態鏈結(DLLs):在 Windows 和 UNIX 平台上常見,這類函式庫不會嵌入到可執行檔中,而是會在程式執行時動態載入。
    • 動態鏈結優點:可以節省磁碟空間和記憶體,因為多個程式可以共享相同的函式庫。當函式庫升級時,所有使用該函式庫的程式會自動更新。
    • 缺點:需要額外的運行時間來動態鏈結,且增加了編譯器、鏈結器和作業系統的複雜性。

7. 動態鏈結的考慮

  • 空間 vs. 時間:動態鏈結能節省磁碟空間與記憶體,但會增加執行時的開銷。
  • 處理升級的能力:更新動態函式庫會影響所有使用該庫的程式,但這需要額外的管理來確保正確的執行。
  • 普遍的做法:動態鏈結會在機器碼層面進行,這意味著 Linker 會處理機器碼中的跳轉與鏈結,而無需了解編譯器的細節。

上一篇
[Day26] CALL: Assembler (2/5)
下一篇
[Day28] CALL: Loader (4/5)
系列文
RISC-V 與處理器之架構學習及應用30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言