4.5 String Table:字符串表
字符串表節區包含以 NULL( ASCII 碼 0) 結尾的字符序列, 通常稱為字符串。 ELF 目標文件通常使用字符串來表示符號和節區名稱。 對字符串的引用通常以字符串在字符串表中的下標給出。
一般, 第一個字節(索引為 0)定義為一個空字符串。類似的,字符串表的最后一個字節也定義為 NULL,以確保所有的字符串都以 NULL 結尾。索引為 0 的字符串在不同的上下文中可以表示無名或者名字為 NULL 的字符串。
允許存在空的字符串表節區,其節區頭部的 sh_size 成員應該為 0。對空的字符串表而言,非 0 的索引值是非法的。
例如:對于各個節區而言,節區頭部的 sh_name 成員包含其對應的節區頭部字符串表節區的索引,此節區由 ELF 頭的 e_shstrndx 成員給出。下圖給出了包含 25 個字節的一個字符串表,以及與不同索引相關的字符串。
上表中包含的字符串如下:
在使用、分析字符串表時,要注意以下幾點:
字符串表索引可以引用節區中任意字節。
字符串可以出現多次
可以存在對子字符串的引用
同一個字符串可以被引用多次。
字符串表中也可以存在未引用的字符串。
4.6 Symbol Table:符號表
目標文件的符號表中包含用來定位、 重定位程序中符號定義和引用的信息。 符號表索引是對此數組的索引。索引 0 表示表中的第一表項,同時也作為未定義符號的索引(即 STN_UNDEF)。
目標文件中的符號表通常是文件中的一個段,段名一般叫做'.symtab'。符號表的結構是 Elf32_sym,每個 Elf32_sym 結構對應一個符號,最后組成一個結構體數組。
1 typedef struct {
2 Elf32_Word st_name; /* 符號名 */
3 Elf32_Addr st_value; /* 符號相對應的值 */
4 Elf32_Word st_size; /* 符號大小 */
5 unsigned char st_info; /* 符號類型和綁定信息 */
6 unsigned char st_other; /* 未使用 */
7 Elf32_Half st_shndx; /* 符號所在的段 */
8 } Elf32_sym;
各個字段的含義如下:
字段 | 說明 |
st_name | 包含目標文件符號字符串表的索引, 其中包含符號名的字符串表示。 如果該值非 0, 則它表示了給出符號名的字符串表索引, 否則符號表項沒有名稱。 注:外部 C 符號在 C 語言和目標文件的符號表中具有相同的名稱。 |
st_value | 此成員給出相關聯的符號的取值。依賴于具體的上下文,它可能是一個絕對值、一個地址等等。 |
st_size | 很多符號具有相關的尺寸大小。 例如一個數據對象的大小是對象中包含的字節數。如果符號沒有大小或者大小未知,則此成員為 0。 |
st_info | 此成員給出符號的類型和綁定屬性。 下面給出若干取值和含義的綁定關系。 |
st_other | 該成員當前包含 0,其含義沒有定義。 |
st_shndx | 每個符號表項都以和其他節區間的關系的方式給出定義。此成員給出相關的節區頭部表索引。某些索引具有特殊含義。 |
4.6.1 st_info符號類型和綁定信息
該成員的低 4 位表示符號的類型(Symbol Type),高 28 位表示符號綁定信息(Symbol Binding),符號表項 st_info 字段合成如下:
/usr/include/elf.h
低 4 位表示符號綁定,用于確定鏈接可見性和行為,具體綁定類型如下:
名稱 | 取值 | 說明 |
STB_LOCAL | 0 | 局部符號在包含該符號定義的目標文件以外不可見。 相同名稱的局部符號可以存在于多個文件中,互不影響。 |
STB_GLOBAL | 1 | 全局符號對所有將組合的目標文件都是可見的。一個文件中對某個全局符號的定義將滿足另一個文件對相同全局符號的未定義引用。 |
STB_WEAK | 2 | 弱符號與全局符號類似,不過他們的定義優先級比較低。 |
STB_LOPROC | 13 | 處于這個范圍的取值是保留給處理器專用語義的。 |
STB_HIPROC | 15 |
在每個符號表中,所有具有 STB_LOCAL 綁定的符號都優先于弱符號和全局符號。符號表節區中的 sh_info 頭部成員包含第一個非局部符號的符號表索引。
符號類型( ELF32_ST_TYPE)定義如下:
名稱 | 取值 | 說明 |
STT_NOTYPE | 0 | 符號的類型沒有指定 |
STT_OBJECT | 1 | 符號與某個數據對象相關,比如一個變量、數組等等 |
STT_FUNC | 2 | 符號與某個函數或者其他可執行代碼相關 |
STT_SECTION | 3 | 符號與某個節區相關。 這種類型的符號表項主要用于重定位,通常具有 STB_LOCAL 綁定。 |
STT_FILE | 4 | 傳統上, 符號的名稱給出了與目標文件相關的源文件的名稱。文件符號具有 STB_LOCAL 綁定,其節區索引是 SHN_ABS, 并且它優先于文件的其他 STB_LOCAL 符號(如果有的話) |
STT_LOPROC | 13 | 此范圍的符號類型值保留給處理器專用語義用途。 |
STT_HIPROC | 15 |
在共享目標文件中的函數符號(類型為 STT_FUNC)具有特別的重要性。當其他目標文件引用了來自某個共享目標中的函數時, 鏈接編輯器自動為所引用的符號創建過程鏈接表項。類型不是 STT_FUNC 的共享目標符號不會自動通過過程鏈接表進行引用。
如果一個符號的取值引用了某個節區中的特定位置,那么它的節區索引成員(st_shndx)包含了其在節區頭部表中的索引。當節區在重定位過程中被移動時,符號的取值也會隨之變化,對符號的引用始終會 '指向' 程序中的相同位置。
【1】弱符號與強符號
在編程中經常碰到一種情況叫符號重復定義。多個目標文件中含有相同名字全局符號的定義,在這些目標文件鏈接的時候將會出現符號重復定義的錯誤。
強符號(Strong Symbol):對于C/C++ 語言來說,編譯器默認函數和初始化了的全局變量為強符號
弱符號(Weak Symbol):未初始化的全局變量為弱符號
可以通過 GCC 的 '__attribute__((weak))' 來定義任何一個強符號為弱符號。需要注意的是,強符號和弱符號都是針對定義來說的,不是針對符號引用。
鏈接器處理強弱符號的規則如下:
規則1:不允許強符號被多次定義(即不同的目標文件中不能有同名的強符號);如果有多個強符號定義,則鏈接器報符號重復定義錯誤
規則2:如果一個符號在某個目標文件中是強符號,在其他文件中都是弱符號,那么選擇強符號
規則3:如果一個符號在所有目標文件中都是弱符號,那么選擇其中占用空間最大的一個
【2】弱引用和強引用
強引用(Strong Reference):我們所看到的對外部目標文件的符號引用在目標文件被最終鏈接成可執行文件時,它們必須要被正確決議,如果沒有找到該符號的定義,鏈接器就會報符號未定義錯誤,這種就稱為強引用。
弱引用(Weak Reference):在處理弱引用的時候,如果該符號有定義,則鏈接器將該符號的引用決議;如果該符號未定義,則鏈接器對于該引用不報錯。
弱引用和強引用主要用于庫的鏈接過程。
在 GCC 中,可以通過符號 '__attribute__((weakref))' 這個擴展關鍵字來聲明對一個外部函數的引用為弱引用。例如:
1 __attribute__ ((weakref)) void foo();
2
3 int main()
4 {
5 if(foo) foo();
6 }
4.6.2 st_shndx:節區索引
某些特殊的節區索引具有不同的語義:
SHN_ABS:符號具有絕對取值,不會因為重定位而發生變化。
SHN_COMMON:符號標注了一個尚未分配的公共塊。符號的取值給出了對齊約束,與節區的 sh_addralign 成員類似。就是說,鏈接編輯器將為符號分配存儲空間,地址位于 st_value 的倍數處。 符號的大小給出了所需要的字節數。
SHN_UNDEF: 此節區表索引值意味著符號沒有定義。當鏈接編輯器將此目標文件與其他定義了該符號的目標文件進行組合時, 此文件中對該符號的引用將被鏈接到實際定義的位置。
4.6.3 STN_UNDEF 符號
符號表中下標為 0(STN_UNDEF)的表項被保留。其中包含如下數值:
名稱 | 取值 | 說明 |
st_name | 0 | 無名稱 |
st_value | 0 | 0 值 |
st_size | 0 | 無大小 |
st_info | 0 | 無類型,局部綁定 |
st_other | 0 | 無附加信息 |
st_shndx | 0 | 無節區 |
4.6.4 st_value:符號取值
不同的目標文件類型中符號表項對 st_value 成員具有不同的解釋:
在可重定位文件中, st_value 中遵從了節區索引為 SHN_COMMON 的符號的對齊約束。
在可重定位的文件中, st_value 中包含已定義符號的節區偏移。 就是說,st_value 是從 st_shndx 所標識的節區頭部開始計算,到符號位置的偏移。
在可執行和共享目標文件中, st_value 包含一個虛地址。為了使得這些文件的符號對動態鏈接器更有用,節區偏移( 針對文 件的解釋)讓位于虛擬地址(針對內存的解釋),因為這時與節區號無關。
盡管符號表取值在不同的目標文件中具有相似的含義, 適當的程序可以采取高效的數據訪問方式。
4.7 重定位信息
重定位是將符號引用與符號定義進行連接的過程。例如,當程序調用了一個函數時,相關的調用指令必須把控制傳輸到適當的目標執行地址。
4.7.1 重定位表
成員 | 說明 |
r_offset | 此成員給出了重定位動作所適用的位置。對于一個可重定位文件而言,此值是從節區頭部開始到將被重定位影響的存儲單位之間的字節偏移。對于可執行文件或者共享目標文件而言, 其取值是被重定位影響到的存儲單元的虛擬地址。 |
r_info | 此成員給出要進行重定位的符號表索引, 以及將實施的重定位類型。 例如一個調用指令的重定位項將包含被調用函數的符號表索引。 如果索引是 STN_UNDEF, 那么重定位使用 0 作為'符號值'。重定位類型是和處理器相關的。當程序代碼引用一個重定位項的重定位類型或者符號表索引, 則表示對表項的 r_info 成員應用 ELF32_R_TYPE 或者 ELF32_R_SYM 的結果。 #define ELF32_R_SYM(i) ((i)>>8) #define ELF32_R_TYPE(i)((unsigned char)(i)) #define ELF32_R_INFO(s, t) (((s)<<8) + (unsigned char)(t) |
r_addend | 此成員給出一個常量補齊, 用來計算將被填充到可重定位字段的數值。 |
如上所述,只有 Elf32_Rela 項目可以明確包含補齊信息。類型為 Elf32_Rel 的表項在將被修改的位置保存隱式的補齊信息。依賴于處理器體系結構,各種形式都可能存在, 甚至是必需的。 因此, 對特定機器的實現可以僅使用一種形式, 也可以根據上下文使用不同的形式。
重定位節區會引用兩個其它節區:符號表、要修改的節區。節區頭部的 sh_info 和 sh_link 成員給出這些關系。不同目標文件的重定位表項對 r_offset 成員具有略微不同的解釋。
在可重定位文件中, r_offset 中包含節區偏移。就是說重定位節區自身描述了如何修改文件中的其他節區;重定位偏移 指定了被修改節區中的一個存儲單元。
在可執行文件和共享的目標文件中, r_offset 中包含一個虛擬地址。為了使得這些文件的重定位表項對動態鏈接器更為有用,節區偏移(針對文件的解釋)讓位于虛地址(針對內存的解釋)。
盡管對 r_offset 的解釋會有少許不同,重定位類型的含義始終不變。
4.7.2 重定位類型
重定位表項描述如何修改后面的指令和數據字段。一般,共享目標文件在創建時,其基本虛擬地址是 0,不過執行地址將隨著動態加載而發生變化。
重定位的過程,按照如下標記:
A 用來計算可重定位字段的取值的補齊。
B 共享目標在執行過程中被加載到內存中的位置(基地址)。
G 在執行過程中, 重定位項的符號的地址所處的位置 —— 全局偏移表的索引。
GOT 全局偏移表( GOT)的地址。
L 某個符號的過程鏈接表項的位置(節區偏移/地址)。過程鏈接表項把函數調用重定位到正確的目標位置。鏈接編輯器構造初始的過程鏈接表,動態鏈接器在執行過程中修改這些項目。
P 存儲單位被重定位(用 r_offset 計算) 到的位置(節區偏移或者地址)。
S 其索引位于重定位項中的符號的取值。
重定位項的 r_offset 取值給定受影響的存儲單位的第一個字節的偏移或者虛擬地址。重定位類型給出那些位需要修改以及如何計算它們的取值。
上一篇:GCC編譯器原理(三)------編譯原理三:編譯過程---預處理
下一篇:GCC編譯器原理(二)------編譯原理一:ELF文件(2)
設計資源 培訓 開發板 精華推薦
- AM2G-2418DZ ±18V 2 瓦 DC-DC 轉換器的典型應用
- EVB-USB3300,用于數碼相機的 USB3300 USB 收發器的評估板
- LTC6247 的典型應用 - 180MHz、1mA 高功率高效雙軌至軌 I/O 運算放大器
- 一種電池供電的無線風速風向數據采集系統
- LT3970IMS-5 3.3V 降壓轉換器的典型應用
- 基于 CY8C24x33 的 BLDC 閉環速度控制
- 使用 Analog Devices 的 LT6654AMPS6-3 的參考設計
- 基于SPV1050能量收集器和電池充電器的薄膜太陽能智能手表
- Si5318-EVB,基于 Si5318 SONET/SDH 精密時鐘乘法器的評估板
- DER-187 - 35 W 電源