目標文件
目標文件格式: 可執行文件,如linux的elf或者windows的pe,他們都是coff的變種;以下幾種文件在linux下采用elf格式;
1.可重定位文件: 這類文件包含代碼和數據。可以用來連接成可執行文件和共享目標文件,靜態鏈接庫也算這一類; 例:hello.o
2.可執行文件: 可以直接執行程序,代表elf可執行文件
3.共享目標文件:包含代碼和數據:鏈接器可以使用這種文件與其他可重定位文件和共享目標文件鏈接,生成新的目標文件;動態鏈接器可以將幾個這種共享目標文件與可執行文件結合。作爲進程映像的一部分來運行;例:lib.so
4.core dump文件:進程意外終止時,系統可以將進程的地址空間的內容以及終止時的一些其他信息轉儲到core dump文件
ELF問價的一些相關段:
.code 或 .text | 保存編譯後的指令 |
---|---|
.data | 保存初始化了的全局靜態變量和局部靜態變量 |
.bss | 爲未初始化的全局靜態變量和局部靜態變量預留位置不佔空間 |
.rodata .rodata1 | 只讀數據段,保存全局const或字符串常量 |
.comment | 保存編譯版本信息 如:字符串 “gcc:(gnu) 4.2.0” |
.debug | 保存調試信息 |
.dynamic | 保存動態鏈接信息 |
.hash | 保存符號哈希表 |
.line | 調試的行號 |
.note | 額外的編譯細膩些,如程序公司 |
.strtab | 字符串表 ,用於保存elf文件中用到的各種字符串 |
.symtab | 符號表 |
.shstrtab | 段名錶 |
.plt .got | 動態鏈接的跳轉表和全局入口表 |
.init .fini | 程序初始化與終結代碼段 |
… | … |
ELF文件查看相關命令:
readelf
objdump
將一個二進制文件作爲目標文件的一個段: 例如圖片
objcopy -I binary -o elf32-i386 -B i386 image.jpg image.o
objdump -ht image.o 查看他的相關符號,可以在程序種直接調用(沒嘗試)
自定義段:
使用 attribute((section(“name”))) 屬性可以把相應的函數和變量放到name所指的段中;
如: attribute((section(“data”))) int global = 10;
elf文件結構描述
1.基本結構圖:
文件頭
1.自定義類型以32位爲例:
Elf32_Addr | 32位版程序地址 | uint32_t |
---|---|---|
Elf32_Half | 32位版短整形 | uint16_t |
Elf32_Off | 32位版偏移地址 | uint32_t |
Elf32_Sword | 32位版有符號整形 | int32_t |
Elf32_Word | 32位版無符號整形 | uint32_t |
2.文件頭結構:
```
typedef struct {
unsigned char e_ident[16];
Elf32_Half e_type;
Elf32_Half e_machine;
Elf32_Word e_version;
Elf32_Addr e_entry;
Elf32_Off e_phoff;
Elf32_Off e_shoff;
Elf32_Word e_flags;
Elf32_Half e_ehsize;
Elf32_Half e_phentsize;
Elf32_Half e_phnum;
Elf32_Half e_shentsize;
Elf32_Half e_shnum;
Elf32_Half e_shstrndx;
}Elf32_Ehdr;
![在這裏插入圖片描述](https://img-blog.csdnimg.cn/20190521214605318.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzM3MjMyMjI4,size_16,color_FFFFFF,t_70)
e_ident 數組給出了 ELF 的一些標識信息,這個數組中不同下標的含義如表所示:
![在這裏插入圖片描述](https://img-blog.csdnimg.cn/20190521214626214.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzM3MjMyMjI4,size_16,color_FFFFFF,t_70)
###### 段表
段表作用:保存段的基本屬性,編譯器、鏈接器和裝載器都是依靠段表來定位和訪問各個段的屬性的;比如:段名,段長,在文件中的偏移,讀寫權限等;
查看命令可用: readelf -S hello.o
段表的描述結構 :
typedef struct {
Elf32_Word sh_name; //section的名字
Elf32_Word sh_type; //section類別
Elf32_Word sh_flags; //section在進程中執行的特性(讀、寫,需分配空間)
Elf32_Addr sh_addr; //在內存中開始的虛地址 如可被加載則爲被加載後的進程地址空間的虛擬地址;否則爲0
Elf32_Off sh_offset; //此section在文件中的偏移(如果存在與文件中),否則無意義
Elf32_Word sh_size; //段長度
Elf32_Word sh_link; //段的鏈接信息
Elf32_Word sh_info; //段的鏈接信息
Elf32_Word sh_addralign; //段地址對齊;都是2的倍數,如果爲0或1則該段沒有段對齊要求
Elf32_Word sh_entsize; //表示固定項的大小,如符號表;如果爲0,表示無固定大小的項
}
sh_type 字段:
![在這裏插入圖片描述](https://img-blog.csdnimg.cn/20190521222131409.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzM3MjMyMjI4,size_16,color_FFFFFF,t_70)
sh_flags:
![在這裏插入圖片描述](https://img-blog.csdnimg.cn/20190521222256526.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzM3MjMyMjI4,size_16,color_FFFFFF,t_70)
SHF_WRITE: 節區包含進程執行過程中將可寫的數據。
SHF_ALLOC: 此節區在進程執行過程中佔用內存。某些控制節區並不出現於目標
文件的內存映像中,對於那些節區,此位應設置爲 0。
SHF_EXECINSTR: 節區包含可執行的機器指令。
SHF_MASKPROC: 所有包含於此掩碼中的四位都用於處理器專用的語義。
sh_link 和 sh_info 字段:
![在這裏插入圖片描述](https://img-blog.csdnimg.cn/20190521233731353.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzM3MjMyMjI4,size_16,color_FFFFFF,t_70)
#### 重定位表
作用: 鏈接器在處理目標文件時,對代碼段或數據段中那些絕對地址的引用需要進行重定位
類型: 例如:.rel.txt就屬於重定位表,類型sh_type爲"SHE_REL"
#### 字符串表
作用: 字符串常見段名爲:“.strtab” 或 “.shstrtab” 這兩個分別是字符串表(放普通字符串)和段表字符串表(保存段表中用到的字符串,最常見段名);
**段表字符串表本身也是elf文件中的一個普通的段,知道他的名字叫做'.shstrtab' .那麼"e_shstrndx" 表示 ".shstrtab” 在段表中的下標,即段表字符串表在段表中的下標**
#### 鏈接的接口--符號
符號表: 目標文件的符號表中包含用來定位、重定位程序中符號定義和引用的信息。符號表 索引是對此數組的索引。索引 0 表示表中的第一表項,同時也作爲 定義符號的索引。
符號 : 我們將函數和變量統稱符號;
符號值:每個目標文件有一個對應的符號表,這個表裏面有記錄目標文件所用到的所有符號,每個定義的符號有一個對應的符號值,對於變量和函數來說就是他們的地址 ;
符號表結構:
typedef struct {
Elf32_Word st_name; /* name - index into string table */
Elf32_Addr st_value; /* symbol value */
Elf32_Word st_size; /* symbol size */
unsigned char st_info; /* type and binding */
unsigned char st_other; /* 0 - no defined meaning */
Elf32_Half st_shndx; /* section header index */
} Elf32_Sym;
![在這裏插入圖片描述](https://img-blog.csdnimg.cn/20190521233831235.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzM3MjMyMjI4,size_16,color_FFFFFF,t_70)
st_info 說明:
st_info 的低四位表示符號綁定,用於確定鏈接可見性和行爲。具體的綁定類型如:
![在這裏插入圖片描述](https://img-blog.csdnimg.cn/20190521233920422.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzM3MjMyMjI4,size_16,color_FFFFFF,t_70)
在每個符號表中,所有具有 STB_LOCAL 綁定的符號都優先於弱符號和全局符 號。符號表節區中的 sh_info 頭部成員包含第一個非局部符號的符號表索引。
st_type 字段:
![在這裏插入圖片描述](https://img-blog.csdnimg.cn/20190521230327899.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzM3MjMyMjI4,size_16,color_FFFFFF,t_70)
st_shndx字段:
SHN_ABS: 符號具有絕對取值,不會因爲重定位而發 生變化。
SHN_COMMON: 符號標註了一個尚 分配的公共 塊。符號的取值給出了對齊約束,與節區的 sh_addralign 成員類似。就是說,鏈接編輯器將爲符號分配存儲空間, 地址位於 st_value 的倍數處。符號的大小給出了所需要的字節數。
SHN_UNDEF: 此節區表索引值意味着符號沒有定義。
當鏈接編輯器將此目標文件與其他定義了該符號的目標 文件進行組合時,此文件中對該符號的引用將被鏈接到實 際定義的位置。
幾個具有代表性的特殊符號:
__executable_start 程序的起始地址,不是入口地址
__etext或-etext或etext 代碼段結束地址
_edata或edata 數據段結束地址
_end 或 end 程序結束地址
符號修飾: 作用防止命名衝突
#### extern "C":
c++使用此函數 : 條件編譯 判斷當前編譯是否爲c++
#ifdef __cplusplus
extern "C" {
#endif
void *memset(void * , int , size_t);
#ifdef __cplusplus
}
#endif
##### 弱符號與強符號
強弱符號:編譯器默認函數和初始化了的全局變量爲強符號,未初始化的全局變量爲弱符號;當多個文件同時定義強符號時將發生重定義錯誤;
規則:
不允許強符號多次定義
如果一個強符號多個弱符號選強符號
如果都是弱符號,選擇佔空間最大的弱符號
定義弱引用好處: 可以在運行時決定使用本地還是庫(如本地定義弱引用函數,當庫中存在此相同函數時用庫的強引用的函數)
弱引用定義:
弱引用全局變量:
__attribute__((weak)) weak = 2;
弱引用外部函數:
__attribute__((weakref)) void foo();
弱引用自己定義的函數:
__attribute__((weak)) void test(int a, int b){ printf( "oo" );}