iT邦幫忙

2021 iThome 鐵人賽

DAY 5
0
Arm Platforms

嵌入式系統建構之知識系列 第 5

Day.5 深入理解連結之Object file

目標文件格式 (Object file)


以文件的方式放在磁碟(Disk)中的 Object module 有三種形式

  • 可重定位目標文件 (Relocatable Object File)
    • 這類文件包含了二進制程式碼和數據,其形式可以再編一時與其他可重新店為目標文件合併起來,建立一個可執行的目標文件
  • 可執行目標文件 (Executable Object File)
    • 包含二進制程式碼和數據,其形式可以直接複製到記憶體並執行
  • 共享的目標文件 (Shared Object File)
    • 一種特殊類型的可重定位目標文件,可在執行時期動態連結並載入到主記憶體,也可在載入到記憶體時期,才動態連結此檔案

目標文件格式


一個程式碼Source code檔案被Compile後,會依照指令和資料部份分開存到Obj File上不同的Section中。

  • .text, .bss, .data 是一般編譯器之預設記憶體區段名稱, linker script 會安排實際的記憶體位址給各個區段.
  • .text 為唯讀區段, 包含 Const string,及Arm 指令程式(程式碼),安置於ROM
  • .data 為可讀寫區段, 放置初值不為 0 的變數 (安置於 RAM中)
    • RAM的內容會在斷電、或是系統重置後消失,Reset的程序需從ROM取得data初值(Copy from ROM to RAM)
  • .bss 為可讀寫區段, 放置初值為 0 的變數. Reset的程序.bss 區段會被清為 0
  • stack 為堆疊區 一般是呼叫函數時的作業區 (返回位址暫存, 傳遞參數, 區域變數和返回值之儲存區), heap 為堆積區 是呼叫 malloc() 時取得記憶區塊的來源.
  • symtal 一個符號表,它存放在程序中定義和引用函數和全域變數的訊息
  • .rel.text 當Linker 把目標文件和其他文件組合時,需要修改那些 text 的位置(Relocation),可執行文件中並不需要重新為訊息,因此通常會省略
  • rel.date 被程序塊引用或定義的所有全域變數的 Relocation 訊息
  • .debug 一個除錯符號表 只有以 -g 選項調用編譯器驅動程式時,才會有這個 section
  • .strtab 字串表
  • .dynamic 動態連結訊息

    其他Section(還有很多未被列出)

符號與符號表


連結的過程就是把多個不同的 obj file 相互"黏"在一起,為了使不同的 obj file 能夠相互黏合,必須有固定的規則才行,才能避免連結的過程中不同變數和函數之間的混淆,在連結中,我們將函數很變數稱為符號(Symbol),連結過程中很關鍵的一個部份就是符號的管理,每一個 obj file 都會有一個相對應的符號表(Symbol Tabel) ,記錄著目標文件中所用到的所有符號,每個定義的符號都有一個對應值,稱為Symbol Value,對於variable和functoin來說,Symbol Value指的就是他們的地址。

還存其他幾個不常用到的符號 :

  • 全局符號 : 定義在目標文件的全域符號,可被其他 obj file 使用
  • 外部符號 : 你有使用,但是卻沒有定義在自己的obj file中 如:printf
  • 段名 : 這個符號的值就是該section的start address 如 .text .data
  • 局部符號 : 像是static variable, 這類的符號對linker來說意義不大,linker往往忽略他們。
  • 行號信息 : obj file 指令與原碼的對應關係,它是可選的

我們可以使用nm指令來查看符號表


連結器如何解析多重定義的全局符號?

在編譯階段, Compiler 會將每個 symbol 分類為 strong 或 weak
Strong Symbol : 包含 procedures 和被初始化過的全域變數。
Weak Symbol : 未被初始化的全域變數。

Linker 利用以下列規則來決定如何做 Linking :

  1. 同時存在多個同名 strong symbol 是不允許的。
  2. 假設有一個 strong symbol 與多個 weak symbol , Linker 應選擇 strong symbol。
  3. 如果只有多個 weak symbol 同名 ,任意選擇其中一個。

Example (規則二)

/* foo3.c */
#include <stdio.h>
void f(void);

int x = 15213; // Strong Symbol

int main(){
    f();
    printf("x = %d\n", x);
    return 0;
}
/* bar3.c */
int x; // Weak Symbol

void f(){
    x = 15212
}

$ gcc -o foobar3 foo3.c bar3.c
$ ./foobar3
x = 15212

在運行時,函數 f 將 x 的值由15212改為15212,因為連結器將選擇定義中的 strong symbol

參考文獻

程序員的自我修養-linker
深入理解計算機系統-linker


上一篇
Day4.Cortex-M 系列 基礎探討
系列文
嵌入式系統建構之知識5

尚未有邦友留言

立即登入留言