目标文件(.o)结构的简单了解
文章目錄
- SimpleSection.o 的結(jié)構(gòu)
- 代碼段 .text
- 數(shù)據(jù)段 .data和只讀數(shù)據(jù)段 .rodata
- .bss段
- 其他段
??????今天來(lái)了解一下在編譯過(guò)程中所產(chǎn)生的的目標(biāo)文件的具體結(jié)構(gòu)。(Ubuntu-20.04 64位虛擬機(jī))
??????采用的源文件 SimpleSection.c 為:
int printf(const char* format,...);int global_init_var = 84; int global_uninit_var;void func1(int i) {printf("%d\n",i); }int main(void) {static int static_var = 85;static int static_var2;int a=1;int b;func1(static_var + static_var2 + a + b);return a; }??????采用命令: gcc -c SimpleSection.c 編譯之后得到目標(biāo)文件 SimpleSection.o 。下面就在這個(gè)目標(biāo)文件上來(lái)分析。
SimpleSection.o 的結(jié)構(gòu)
?????? 使用 binutils 的工具 objdump 來(lái)查看目標(biāo)文件的結(jié)構(gòu),運(yùn)行命令:objdump -h SimpleSection.o 將目標(biāo)文件各個(gè)段的基本信息打印出來(lái):
?????? 關(guān)于段的幾個(gè)重要屬性:Size 表示段的長(zhǎng)度,File off 表示段的位置,每個(gè)段的第2行中的 “CONTENTS” 表示該段在文件中存在。在 bss 段中沒(méi)有 “CONTENTS” ,表示該段在目標(biāo)文件中不存在。.note.GNU-stack 堆棧段的長(zhǎng)度為0,在這里忽略掉它,認(rèn)為它也不存在。
?????? 用 size 命令可以查看 ELF 文件的代碼段、數(shù)據(jù)段和 bss 段的長(zhǎng)度(dec 表示3個(gè)段長(zhǎng)度的和的十進(jìn)制,hex表示長(zhǎng)度和的十六進(jìn)制) 如下圖。
??????為什么 size 和 objdump 查看目標(biāo)文件的 .text 段的大小不一樣呢?
??????因?yàn)閟ize默認(rèn)是運(yùn)行在"Berkeley compatibility mode"下。在這種模式下,會(huì)將不可執(zhí)行的擁有"ALLOC"屬性的只讀段歸到.text段下,很典型的就是.rodata段。而在我們這個(gè)例子中,使用 size 命令得到的 text 段長(zhǎng)度 = .text + .rodata + .note.gnu.property + .eh_frame 。如果你使用"size -A obj.o",那么size會(huì)運(yùn)行在"System V compatibility mode",此時(shí),用objdump -h和size顯示的.text段大小就差不多了,如下圖。
代碼段 .text
??????程序源代碼編譯之后的機(jī)器指令經(jīng)常被放在代碼段中。通過(guò)使用 -s -d 參數(shù)的 objdump 命令將所有段以十六進(jìn)制的形式打印出來(lái)(-s),并將所有包含指令的段反匯編(-d)。如下:
??????以及反匯編之后的代碼段內(nèi)容:
??????Contents of section .text 就是將 .text 的數(shù)據(jù)以十六進(jìn)制方式打印出來(lái),總共 0x5f 字節(jié),與前面我們得到的 .text 段長(zhǎng)度相符合,最左邊一列表示偏移量,中間四列為十六進(jìn)制內(nèi)容,最右邊一列是 .text 的ASDII 碼形式。對(duì)照下面的反匯編結(jié)果,可以得到,.text 段中包含的正是SimpleSection.c 中兩個(gè)函數(shù) func1() 和 main() 的指令。其中 func1() 函數(shù)在前,main() 在后。
數(shù)據(jù)段 .data和只讀數(shù)據(jù)段 .rodata
??????.data 段保存的是那些已經(jīng)初始化了的全局靜態(tài)變量和局部靜態(tài)變量。在我們這個(gè)例子 SimpleSection.c 中,global_init_var 和 static_var 是已經(jīng)初始化過(guò)的,每個(gè)變量 4 個(gè)字節(jié),一共 8 個(gè)字節(jié)被存儲(chǔ)到 .data 段中。在這里采用小端法來(lái)存儲(chǔ),.data 段中前四個(gè)字節(jié)為 5400 0000 ,轉(zhuǎn)換為十進(jìn)制為 84;后四個(gè)字節(jié)為 5500 0000 ,轉(zhuǎn)換為十進(jìn)制為 85。分別與這兩個(gè)變量的值一一對(duì)應(yīng)。
??????.rodata 段中存放的是只讀數(shù)據(jù),一般是程序中的只讀變量(如 const 修飾的變量)和字符串常量。在 SimpleSection.c 中調(diào)用 printf 時(shí),用到了一個(gè)字符串常量 “%d\n”,它是一種只讀數(shù)據(jù),被存儲(chǔ)到 .rodata 段中。.rodata 段中的四個(gè)字節(jié) 2564 0a00 分別對(duì)應(yīng)的是字符 ‘%’、‘d’、’\n’ 和 ‘\0’ 。
??????有時(shí)候編譯器也會(huì)將字符串常量放在 .data 段中。
.bss段
??????.bss 段中存放的是未初始化的全局變量和局部靜態(tài)變量,在上述代碼中,global_uninit_var 和 static_var2 是未被初始化過(guò)的,它們被存放在 .bss 段中,更準(zhǔn)確的來(lái)講,是在 .bss 段為它們預(yù)留空間。可以認(rèn)為未初始化過(guò)的變量值為0,而存儲(chǔ) 0 是沒(méi)有必要的。.bss 段沒(méi)有實(shí)際內(nèi)容,所以它在可執(zhí)行文件中也不占據(jù)空間。
??????上面得到的 .bss 段大小為 4 個(gè)字節(jié),與變量 global_uninit_var 和 static_var2 的大小之和 8 個(gè)字節(jié)不符。實(shí)際上通過(guò)符號(hào)表(Symbol Table)能夠看到,只有 static_var2 被放在了 .bss 段中,而 global_uninit_var 未被放在任何段,只是一個(gè)未定義的 “COMMON 符號(hào)”。這和不同的語(yǔ)言不同的編譯器有關(guān),有的編譯器會(huì)將 全局未初始化變量 存放在目標(biāo)文件 .bss 段中,有些則不存放,只是預(yù)留一個(gè) 未定義的全局變量符號(hào),等到最終鏈接為可執(zhí)行文件時(shí)再在 .bss 段中分配空間。原則上來(lái)講,可以簡(jiǎn)單的認(rèn)為全局未初始化變量被存放在 .bss 段中。但是未初始化的靜態(tài)變量(編譯單元內(nèi)部可見(jiàn))的確是存放在 .bss 段中的。
其他段
??????.comment 段中存放的是編譯器版本信息。.其余兩個(gè)段 .note.gnu.property 和 .eh_frame 在此不做說(shuō)明。
總結(jié)
以上是生活随笔為你收集整理的目标文件(.o)结构的简单了解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 静态程序分析chapter5 - 常量传
- 下一篇: python-virtualenv虚拟环