1. 程式人生 > >linux 目標檔案(*.o) bss,data,text,rodata,堆,棧

linux 目標檔案(*.o) bss,data,text,rodata,堆,棧

linux目標檔案

一個簡單的程式被編譯成目標檔案後的結構如下:


從圖可以看出,已初始化的全域性變數和區域性靜態變數儲存在 .data段中,未初始化的全域性變數和未初始化的區域性靜態變數儲存在 .bss段中。

目標檔案各個段在檔案中的佈局如下:


各個段介紹:

init段:

程式初始化入口程式碼,在main() 之前執行。

bss段:

BSS段屬於靜態記憶體分配。通常是指用來存放程式中未初始化的全域性變數和未初始化的區域性靜態變數。未初始化的全域性變數和未初始化的區域性靜態變數預設值是0,本來這些變數也可以放到data段的,但是因為他們都是0,所以為他們在data段分配空間並且存放資料0是沒有必要的。  

程式在執行時,才會給BSS段裡面的變數分配記憶體空間。 

在目標檔案(*.o)和可執行檔案中,BSS段只是為未初始化的全域性變數和未初始化的區域性靜態變數預留位置而已,它並沒有內容,所以它不佔據空間。

section table中儲存了BSS段(未初始化的全域性變數和未初始化的區域性靜態變數)記憶體空間大小總和。 (objdump -h *.o 命令可以看到)

data段:

資料段(datasegment)通常是指用來存放程式中已初始化的全域性變數和已初始化的靜態變數的一塊記憶體區域。資料段屬於靜態記憶體分配。

text段:

程式碼段(codesegment/textsegment)通常是指用來存放程式執行程式碼的一塊記憶體區域。這部分割槽域的大小在程式執行前就已經確定,並且記憶體區域通常屬於只讀

,某些架構也允許程式碼段為可寫,即允許修改程式。在程式碼段中,也有可能包含一些只讀的常數變數,例如字串常量等。

rodata

存放的是隻讀資料,比如字串常量,全域性const變數 和 #define定義的常量。例如: char*p="123456", "123456"就存放在rodata段中。

strtab段:

儲存的是變數名,函式名等。例如: char* szPath="/root",void func()  變數名szPath 和函式名func 儲存在strtab段裡。

shstrtab段:

bss,text,data等段名也儲存在這裡。

rel.text段:

針對 text段的重定位表,還有 rel.data(針對data段的重定位表)

heap堆:

堆是用於存放程序執行中被動態分配的記憶體段,它的大小並不固定,可動態擴張或縮減。當程序呼叫malloc等函式分配記憶體時,新分配的記憶體就被動態新增到堆上(堆被擴張);當利用free等函式釋放記憶體時,被釋放的記憶體從堆中被剔除(堆被縮減)

stack棧:

是使用者存放程式臨時建立的區域性變數,也就是說我們函式括弧“{}”中定義的變數(但不包括static宣告的變數,static意味著在資料段中存放變數)。除此以外,在函式被呼叫時,其引數也會被壓入發起呼叫的程序棧中,並且待到呼叫結束後,函式的返回值也會被存放回棧中。由於棧的先進先出特點,所以棧特別方便用來儲存/恢復呼叫現場。從這個意義上講,我們可以把堆疊看成一個寄存、交換臨時資料的記憶體區。

驗證BSS記憶體空間

程式1:

int ar[30000];
void main()
{

    ......

}

程式2:

int ar[300000] =  {1, 2, 3, 4, 5, 6 };
void main()
{

    ......

}

    結論是:程式2編譯之後所得的.exe檔案比程式1的要大得多。 為什麼?

區別很明顯,一個位於.bss段,而另一個位於.data段,兩者的區別在於

l          全域性的未初始化變數存在於.bss段中,具體體現為一個佔位符;全域性的已初始化變數存於.data段中;

l          而函式內的自動變數都在棧上分配空間。

l          .bss是不佔用.exe檔案空間的,其內容由作業系統初始化(清零);

l          而.data卻需要佔用,其內容由程式初始化,因此造成了上述情況。

注意:

1. bss段(未手動初始化的資料)並不給該段的資料分配空間. 程式執行後,系統分配記憶體空間並由系統初始化,預設記憶體空間的值都為0.  section table中儲存了BSS段(未初始化的全域性變數和未初始化的區域性靜態變數)記憶體空間大小總和,所以程式執行後,系統知道該分配多少記憶體給BSS段。

2. data(已手動初始化的資料)段則為資料分配空間,資料儲存在目標檔案中。

這裡有個疑問: data段是變數的記憶體空間,那是如何區分哪幾個位元組是變數a的? 哪幾個位元組是變數b的? 因為變數a,b的記憶體空間是在一起的,如果你不告訴他們的型別,我們的確是不知道變數a有幾個位元組,變數b有幾個位元組。

那麼哪裡儲存了 變數a,b的型別了?  查資料發現,text程式碼段中呼叫a的彙編程式碼,是會告訴我們變數a的型別的,這樣我們就知道讀取哪幾個位元組的值了。 程式執行起來後,BSS段中變數記憶體資料讀取原理類似。