天天看點

Linux之連結腳本

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段屬于靜态記憶體配置設定,即程式一開始就将其清零了)。

繼續閱讀