GNU LD 手冊略讀 (0): 目錄和簡介
關於一個程式的binary要怎麼存放其實是很有趣的問題,我以前都沒有去想這個問題。後來當組裝工久了以後就忍不住會想知道這些。隨便想一下就有很多問題,例如:
- 程式碼和資料要怎麼放?
- 怎麼做到不同的source code共用global 變數?
- global 變數和local變數放的地方應該不一樣吧?那麼確實不一樣的點是?
- 呼叫副函數這回事一定是要先找到副函數再跳過去吧?那麼「找到」到底是什麼意思?
- 如果是用shared library的話,runtime才會找到副函數所在的地方,那麼為什麼編譯的時候不會有錯誤呢? ...
這些問題列出來真的是「罄竹難書」,不過我想整體來說至少在Linux下面從binutils下手應該是沒錯。第一個問題應該和linker有關係。所以我先去看GNU ld手冊的linker script部份,希望可以解決我的疑惑。就算和我的問題無關,至少可以留下一些中文參考資料,造福需要的朋友。
為了讓單篇的篇幅不要太過冗長,我把內容切割成幾個部份。這邊就先放全部的目錄和簡介的部份。
目錄
簡介
ld
是GNU linker的程式。ld
吃多個object (.o)檔或archive (.a)檔,將他們的資料relocate還有symbol reference資訊一併輸出到新的binary。link通常是compile產生binary的最後步驟。ld
在執行的時候依照Linker command language檔案描述去產生binary。ld支援不同的binary format (BFD: Binary File Descriptor)
每次link的時候,都會依照特定的命令去產生新的object檔。而這些命令就是linker script;換句話說,linker script提供一連串的命令讓linker照表操課。Linker script描述的命令有
- ld吃的object檔案中的section要怎麼map到要輸出的binary檔案。
- 要輸出的binary檔案要在記憶體中的layout
- 其他
因為每次link一定會依據linker script去link,所以當ld
沒有指定linker script的時候,系統會使用預設的linker script。而ld --verbose
可以顯示預設的linkder script。link時指定自幹的linker script則使用ld -T 自己的linker script
。
背景知識
- object 檔格式:輸入檔案和輸出檔案所遵循的格式
- object 檔案:linker處理時讀入的輸入檔案和將結果存放的輸出檔案
- executable:ld輸出的檔案,有時候會這樣稱呼
- 每個object檔案都有好幾個section,而
- input section:輸入object檔案中的section
- output section:輸出object檔案中的section
- bss
- 存放沒有初始值的全域變數的地方 ex:
int g_var;
- text
- data
- 存放沒有初始值全域變數的地方 ex:
int g_var = 0xdeadbeef;
- locale counter
- region
- 執行平臺實體的記憶體區塊。如0x1000~0x1999是ROM, 0x5000~0x9999是RAM。那麼這個平臺就可以設定成有兩個region。
Section
- obj檔案內部有一組section
- section包含
- 自己的名稱
- section contents
- section長度資訊
- 狀態
- loadable: 執行時該section是否需要被載入到記憶體
- allocatable: 如果section本身沒資料(如.bss)可以設成這個狀態,讓loader先保留記憶體的一塊空間
- section不是loadable 或allocatable 的話一般來說都是給debug用的
objdump -h
顯示的狀態(出處),不要問我為何和手冊不一樣,因為我也不知道。
LOAD
DATA
READONLY
ALLOC
- 表示該section會吃記憶體,你可能會想說廢話,section不放記憶體放檔案是放心酸的嘛?還真的有,例如放除錯的section。
CONTENTS
- 表示這個section是執行程式所需要的資訊,如程式碼或是資料。
- 參考示意圖: (Jim Huang) How GNU Toolchain Works投影片
Section 記憶體位址
- Output section如果被載入記憶體,會存放兩種記憶體位址
- VMA: Virtual Memory Address
- Runtime 的記憶體位址
- LMA: Load Memory Address
- 一般來說,VMA = LMA。不同情況有東西要燒到ROM時參考LMA。從ROM載入到記憶體執行的時候參考VMA
- 可以使用objdump -h看VMA/LMA資訊
Symbol
- 一個object 檔案存放多個symbol,又稱為symbol table
- 將名稱對應到一個記憶體位址的symbol稱為defined symbol,名稱沒有對應到記憶體位址的稱為undefined symbol
- 名稱通常就是全域變數、靜態變數或是函數的名稱
- 一般來說,如果把單獨的c編譯成object file時
- defined symbol為該檔案內的global variable, static varible 和funciton
- undefined symbol為該檔案內的extern variable和外部funciton
- 可以使用objdump -t或是nm看symbol資訊