1. 什麼是鏈接腳本
鏈接器主要有兩個作用,一是將若干輸入文件(.o文件)根據一定規則合併爲一個輸出文件(例如ELF格式的可執行文件);二是將符號與地址綁定。本文只關心它的第一個功能,即如何根據一定規則將一個或多個輸入文件合併成輸出文件。這裏的“一定規則”是通過鏈接腳本描述的。鏈接器有一個編譯到其二進制代碼中的默認鏈接腳本(可以使用–verbose命令行顯示缺省的鏈接器腳本的內容),大多數情況下使用它鏈接輸入文件並生成目標文件。當然,我們也可以提供自定義的腳本以精確控制目標文件的格式,如同Linux內核做得那樣,鏈接器“- T”參數用於指定自定義的腳本文件。
2.簡單的鏈接腳本示例
鏈接腳本由一系列命令組成, 每個命令由一個關鍵字(一般在其後緊跟相關參數)或一條對符號的賦值語句組成. 命令由分號‘;’分隔開.
假設你的程序只有代碼段,初始化過的數據段,和未初始化過的數據段.這些會存在於‘.text’,‘data’,‘bss’段中.
對於這個例子,假設代碼應該被載入到地址0x1000處,而數據應該從0x8000000開始,如下是實現這個功能的腳本:
SECTIONS
{
.=0x1000;
.text:{*(.text)}
.=0x8000000;
.data:{*(.data)}
.bss:{*(.bss)}
}
具體分析:
關鍵字SECTIONS開始於這個配置.後面跟有一串放在花括號中的符號賦值和輸出端描述的內容.
第一行是對一個特殊的符號‘.’賦值,這是一個定位標識器.如果你沒有以其他的方式制定輸出段的地址,那地址值就會被設爲定位標識器的現有值,即0x1000.
第二行定義一個輸出段,‘.text’.冒號‘:’是語法需要,現在可以被忽略.段後面的花括號中,應該列出所有應該放入這個輸出段中的輸入端的名字. '*’是通配符,匹配所有文件名.即將所有輸入文件中的.text段都保存在此段中.
餘下的是.data和.bss段,同理,鏈接器會把所有.data段從地址0x8000000開始處放置.
最後,定位標識器的值變爲0x8000000加上所有.data段的地址.此時鏈接器把所有.bss放在此處開始的地址.
3.簡單的鏈接腳本命令
設置入口點
在運行一個程序時,第一個被執行到的指令成爲‘入口點’.你可以使用‘ENTRY’鏈接腳本命令來設置入口點.參數是一個符號名,如下:
ENTRY(SYMBOL)
有很多不同的方法來設置入口點.鏈接器會通過按順序嘗試一下方法來設置入口點.
1,‘-e’入口命令行選項
2,鏈接腳本中的ENTRY(SYMBOL)命令
3,如果定義了start,就使用start的值
4,如果存在就使用‘.text’段的首地址
5,地址‘0’
4.基本概念
bss段:
BSS段(bsssegment)通常是指用來存放程序中未初始化的全局變量的一塊內存區域。BSS是英文BlockStarted by Symbol的簡稱。BSS段屬於靜態內存分配。
data段:
數據段(datasegment)通常是指用來存放程序中已初始化的全局變量的一塊內存區域。數據段屬於靜態內存分配。
text段:
代碼段(codesegment/textsegment)通常是指用來存放程序執行代碼的一塊內存區域。這部分區域的大小在程序運行前就已經確定,並且內存區域通常屬於只讀,某些架構也允許代碼段爲可寫,即允許修改程序。
rodata段:
存放C中的字符串和#define定義的常量
heap堆:
堆是用於存放進程運行中被動態分配的內存段,它的大小並不固定,可動態擴張或縮減。當進程調用malloc等函數分配內存時,新分配的內存就被動態添加到堆上(堆被擴張);當利用free等函數釋放內存時,被釋放的內存從堆中被剔除(堆被縮減)
stack棧:
是用戶存放程序臨時創建的局部變量,也就是說我們函數括弧“{}”中定義的變量(但不包括static聲明的變量,static意味着在數據段中存放變量)。除此以外,在函數被調用時,其參數也會被壓入發起調用的進程棧中,並且待到調用結束後,函數的返回值也會被存放回棧中。由於棧的先進先出特點,所以棧特別方便用來保存/恢復調用現場。從這個意義上講,我們可以把堆棧看成一個寄存、交換臨時數據的內存區。
常量段:
常量段一般包含編譯器產生的數據(與只讀段包含用戶定義的只讀數據不同)。比如說由一個語句a=2+3編譯器把2+3編譯就算出5,存成常量5在常量段中
text和data段都在可執行文件中(在嵌入式系統裏一般是固化在鏡像文件中),由系統從可執行文件中加載; 而bss段不在可執行文件中,由系統初始化。
一般情況下,一個程序本質上都是由 bss段、data段、text段三個組成的——本概念是當前的計算機程序設計中是很重要的一個基本概念。而且在嵌入式系統的設計中也非常重要,牽涉到嵌入式系統運行時的內存大小分配,存儲單元佔用空間大小的問題。
在採用段式內存管理的架構中(比如intel的80x86系統),bss段(Block Started by Symbol segment)通常是指用來存放程序中未初始化的全局變量的一塊內存區域,一般在初始化時bss 段部分將會清零(bss段屬於靜態內存分配,即程序一開始就將其清零了)。