編譯后的目標文件包含機器指令惠桃、數(shù)據(jù)和鏈接所需要的一些信息浦夷,比如符號表、調(diào)試信息辜王、字符串等劈狐。目標文件將這些信息按不同的屬性,以段(Segment)的形式存儲呐馆。
從廣義上看肥缔,目標文件與可執(zhí)行文件的格式是一樣的,只是還沒經(jīng)過鏈接的過程汹来。Windows下的可執(zhí)行文件格式是PE续膳,Linux是ELF,它們都是COEF格式的變種收班。下文的剖析以ELF結(jié)構(gòu)為主坟岔。
目標文件的結(jié)構(gòu)
圖片.png
- ELF文件頭:
包含描述整個文件的基本屬性,比如文件版本摔桦、目標機器型號等社付。 - 段表(Section Table):
除文件頭以外最重要的結(jié)構(gòu),描述了各個段的信息邻耕,比如段名瘦穆、段長度、在文件中的偏移赊豌、讀寫權(quán)限和其他屬性扛或。
編譯器、鏈接器和裝載器都是依靠段表定位和訪問各個段的屬性的碘饼。
段表的位置由文件頭的“e_shoff”成員決定熙兔,圖中位于偏移0x118悲伶。
段名對于編譯器、鏈接器有意義住涉,但對于操作系統(tǒng)無實際意義麸锉,操作系統(tǒng)的處理由段類型和段的標志位決定。 - 代碼段(常見的名字.code舆声、.text)
編譯后的機器指令放在代碼段花沉。 - 數(shù)據(jù)段(.data)
已初始化的全局變量和靜態(tài)變量放在數(shù)據(jù)段。 - 只讀數(shù)據(jù)段(.rodata)
存放只讀數(shù)據(jù)媳握,一般是程序里的只讀變量(如const)和字符串常量(有些編譯器放在數(shù)據(jù)段)碱屁。好處有:語義上支持C++ const關(guān)鍵字;安全蛾找,操作系統(tǒng)加載的時候?qū)傩杂成涑芍蛔x娩脾,防止修改;支持只讀存儲器(ROM)訪問打毛。
- .bss段
.bss段為未初始化的全局變量和靜態(tài)變量(初始化為0也默認為未初始化)預留位置柿赊,它并沒有內(nèi)容,也不占空間幻枉。
有些編譯器不存放碰声,只是在符號表預留一個符號,鏈接的時候再在.bss段分配空間熬甫。 - 重定位表(.rel.text)
重定位的信息記錄在重定位表里奥邮,每個要重定位的代碼段或數(shù)據(jù)段,都有一個相應的重定位表罗珍。 - 字符串表
.strtab是字符串表洽腺,保存普通字符串,比如符號名字覆旱;.shstrtab是段表字符串表蘸朋,保存段表中用到的字符串,比如段名扣唱。
字符串的長度往往是不定的藕坯,固定表示它比較困難。一種常見的做法是集中存放到一個表里噪沙,然后用偏移來表示炼彪。
- 符號表(.symtab)
在鏈接中,我們將函數(shù)和變量統(tǒng)稱為符號(Symbol)正歼,函數(shù)名和變量名就是符號名辐马。此外,符號還包括文件名局义、段名和行號(可選)喜爷。
每個目標文件都有一個符號表冗疮,記錄了所有符號。每個符號有一個對應的符號值檩帐,對于函數(shù)和變量來說术幔,符號值就是地址
為什么要把指令和數(shù)據(jù)分開存放
- 程序裝在后,數(shù)據(jù)和指令被映射到兩塊不同的區(qū)域湃密,可以設(shè)置指令只讀诅挑,數(shù)據(jù)可讀寫。
- 提高程序的局部性泛源,從而提高緩存命中率拔妥。
- 最重要的原因是,指令共享俩由。