Linux之ELF文件初探 ---(elf源码)
ELF Format 筆記(十)—— 重定位(relocation)
https://www.cnblogs.com/ilocker/p/4904942.html? ?---源碼分析
?
Linux之ELF文件初探
https://www.cnblogs.com/TJTO/p/11470294.html?---源碼下載
?
ELF文件結構描述
https://www.cnblogs.com/linhaostudy/p/8855238.html
?
靜態程序編譯鏈接與裝載(三)segment分析以及鏈接視圖與執行視圖
https://blog.csdn.net/weiqi7777/article/details/87477345
?
ELF中可以被修改又不影響執行的區域
https://www.jianshu.com/p/9ce47c938706
?
靜態程序編譯鏈接與裝載(三)segment分析以及鏈接視圖與執行視圖?
http://blog.chinaaet.com/weiqi7777/p/5100061651
?
-
對比windowsPE文件與概述
在windows中可執行文件是pe文件格式,Linux中可執行文件是ELF文件,其文件格式是ELF文件格式,在Linux下的ELF文件除了可執行文件(Excutable File),可重定位目標文件(RellocatableObject File)、共享目標文件(SharedObjectFile)、核心轉儲文件(Core DumpFile)也都是ELF格式文件。
一個典型的ELF文件大致的結構如下
| 程序頭表(Program Header Table) |
| 代碼段(.text) |
| 數據段(.data) |
| bss段(.bss) |
| 段表字符串表(.shstrtab) |
| 段表(Section Header Table) |
| 符號表(.symtab) |
| 字符串表(.strtab) |
| 重定位表(.rel.text) |
| 重定位表(.rel.data) |
-
使用Linux下專用工具readelf來查看elf文件信息
? ??
?
- 查看readelf中的源碼
?
?
?
FLF文件組成
文件頭:
用于記錄一個ELF文件的信息(多少位?能夠運行的CPU平臺是什么?程序的入口點在哪里)
-
查看ELF頭
?
?
?在readelf的源碼中變量類型Elf_Internal_Ehdr_,定義在internal頭文件中
?
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #define EI_NIDENT??????? 16??????????????? /* Size of e_ident[] */ ? typedef?struct?elf_internal_ehdr { ??unsigned?char????????????????e_ident[EI_NIDENT];???/* ELF "magic number" */ ??bfd_vma????????????????????? e_entry;??????????????/* Entry point virtual address */ ??bfd_size_type??????????????? e_phoff;??????????????/* Program header table file offset */ ??bfd_size_type??????????????? e_shoff;??????????????/* Section header table file offset */ ??unsigned?long????????????????e_version;????????????/* Identifies object file version */ ??unsigned?long????????????????e_flags;??????????????/* Processor-specific flags */ ??unsigned?short???????????????e_type;???????????????/* Identifies object file type */ ??unsigned?short???????????????e_machine;????????????/* Specifies required architecture */ ??unsigned?int?????????????????e_ehsize;?????????????/* ELF header size in bytes */ ??unsigned?int?????????????????e_phentsize;??????????/* Program header table entry size */ ??unsigned?int?????????????????e_phnum;??????????????/* Program header table entry count */ ??unsigned?int?????????????????e_shentsize;??????????/* Section header table entry size */ ??unsigned?int?????????????????e_shnum;??????????????/* Section header table entry count */ ??unsigned?int?????????????????e_shstrndx;???????????/* Section header string table index */ } Elf_Internal_Ehdr; |
-
- 在Linux自帶的頭文件中查看
?
?
?
#define EI_NIDENT (16)typedef struct{unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */Elf32_Half e_type; /* Object file type */Elf32_Half e_machine; /* Architecture */Elf32_Word e_version; /* Object file version */Elf32_Addr e_entry; /* Entry point virtual address */Elf32_Off e_phoff; /* Program header table file offset */Elf32_Off e_shoff; /* Section header table file offset */Elf32_Word e_flags; /* Processor-specific flags */Elf32_Half e_ehsize; /* ELF header size in bytes */Elf32_Half e_phentsize; /* Program header table entry size */Elf32_Half e_phnum; /* Program header table entry count */Elf32_Half e_shentsize; /* Section header table entry size */Elf32_Half e_shnum; /* Section header table entry count */Elf32_Half e_shstrndx; /* Section header string table index */} Elf32_Ehdr;程序頭表:
記錄了每個Segment的相關信息,比如類型、對應文件的偏移、大小、屬性等,
程序頭表和段頭表相對獨立,它們是由ELF文件頭統一管理,程序頭表管理ELF文件加載后,ELF文件內可加載段到內存映像的映射關系,一般只有可執行文件中,包含程序頭表。程序頭表包含多個程序頭表項,程序頭表描述的對象稱為“Segment”,Segment描述的是ELF文件加載后的數據塊,段(Section)描述的是ELF文件加載前的數據塊。一般來說,來說兩者會存在一定的對應關系,比如代碼段.text的加載信息保存在程序頭表項對應存放代碼的Segment中,數據段.data的加載信息保存在程序頭表項對應存放數據的Segment中。有時候為了簡化程序頭表項的個數,會把同類型的多個段,設置整個ELF文件作為一個Segment
-
程序頭表的數據結構
?
p_flag權限屬性標志
| 1 | 可執行 | PE_X |
| 2 | 可寫 | PE_W |
| 3 | 可讀 | PE_R |
區段頭表:用于記錄ELF文件的主要的數據
-
查看區段
-
區段頭表的數據結構
?
區段頭表一共有10個字段,含義如下
(1)sh_name段名,是一個是一個4字節的偏移,記錄了段名字符串在段表字符串表(“.shstrtab”段)內的偏移。段表字符串并非表的形式,而是一個文件塊,保存了所有的段表字符串內容,存儲在“.shstrtab”的段中,根據“.shstrtab”的偏移,加上sh_name便可以訪問到每個段對應的段名字符串。
?
起始地址是000017ac,第一個段表項全0,sh_name在段表項中的偏移是001b,由上圖可以得到“.shstrtab”段的偏移是0016ae,所以,計算段名的偏移應該是0x0000001b + 0x000016ae = 0x000016c9
根據計算的結果,查看0x000016c9處:
(2)sh_type,表示段的類型。段的類型有很多,常見的有SHT_PROGBITS,表示程序數據,SHT_SYMTAB表示符號表,SHT_STRTAB表示字符串表,還有專門存放構造函數數組段SHT_INIT_ARRAY,析構函數數組段SHT_FINI_ARRAY。
-
-
.txt 代碼段
-
.data 數據段
-
.radata記錄常量數據
-
.symtab記錄符號表(相當于PE文件的導出表)的數據
-
.strtab 串表段
-
.shstrtab 有段表 字符串表段
-
.rel .plt記錄某個區段的重定位內容(相當于PE文件的導入表)
-
對應的宏如下:
1 /* Legal values for sh_type (section type). */2 3 #define SHT_NULL 0 /* Section header table entry unused */4 #define SHT_PROGBITS 1 /* Program data */5 #define SHT_SYMTAB 2 /* Symbol table */6 #define SHT_STRTAB 3 /* String table */7 #define SHT_RELA 4 /* Relocation entries with addends */8 #define SHT_HASH 5 /* Symbol hash table */9 #define SHT_DYNAMIC 6 /* Dynamic linking information */ 10 #define SHT_NOTE 7 /* Notes */ 11 #define SHT_NOBITS 8 /* Program space with no data (bss) */ 12 #define SHT_REL 9 /* Relocation entries, no addends */ 13 #define SHT_SHLIB 10 /* Reserved */ 14 #define SHT_DYNSYM 11 /* Dynamic linker symbol table */ 15 #define SHT_INIT_ARRAY 14 /* Array of constructors */ 16 #define SHT_FINI_ARRAY 15 /* Array of destructors */ 17 #define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */(3)sh_flags,表示段標志,記錄段的屬性。其中0表示默認屬性,1表示段可寫,取值位SHF_WRITE。2表示段加載后需要為之分配內存空間,取值為SHF_ALLOC。4表示可執行,取值為SHF_EXECINSTR,段標志屬性可以疊加。
(4)sh_addr,表示段加載后的線性地址
(5)sh_offset,表示段在文件內的偏移,根據此偏移可確定段的位置,讀取段的內容。
(6)sh_size,表示段的大小,單位為字節。需要注意的是,如果段類型為SHT_NOBITS,段內沒有數據,那么段的大小并非指文件塊的大小,而是指段加載后占用內存的大小。
(7~8)sh_link和sh_info表示段的鏈接信息,一般用于描述符號表段和重定位表段的鏈接信息。對于符號表段(SHT_SYMTAB),sh_link記錄的是符號表使用的串表所在段(一般是,.strtab)對應段表項在段表內的索引。
sh_info 記錄的是符號表最后一個局部符號的符號表項在符號表內的索引加1,一般恰好是第一個全局符號的符號表項索引,這樣可以幫助連接器更快的地定位到第一個全局符號。如下圖:段中符號表段的信息sh_info,剛好是局部符號+1的索引。
對于重定位表表段(段類型是SHT_REL),sh_link記錄重定位所作用的符號表段表項早段內的索引,而sh_info記錄重定位所作用的段對應的段表項在段表中的索引。
| SHT_DYNAMIC | 此表項中條目所用到的字符串表在段表中的索引 | ? |
| SHT_HASH | 此哈希表所適用的符號表的段表索引 | ? |
| SHT_REL | 相關符號表的段表索引 | 重定位所使用的段的段表索引 |
| SHT_RELA | 相關聯的字符串表的段表索引 | 最后一個局部符號的符號表索引值+1 |
| 其它 | SHN_UNDEF | 0 |
(9)sh_addralign,表示段的對齊方式,對齊規則為 sh_offset % sh_addralign = 0,即段的文件偏移必須是sh_addralign的整數倍,sh_addralign的取值必須是2的整數倍,入1、2、4、8等。
| 0 | 無對齊要求 | ? |
| 1 | 無對齊要求 | ? |
| 4 | 對齊4 | 滿足sh_iffset % 4 = 0 |
| 16 | 對齊16 | 滿足sh_iffset % 16 = 0 |
| 32 | 對齊32 | 滿足sh_iffset % 32 = 0 |
(10) sh_entsize,一般用于保存注入符號表段,重定位表段時,表示段內保存表的表項大小。例如符號表段“.symtab”內保存的符號表的表項大小為sizeof(Elf32——sym)= 16字節,重定位表段“.rel.plt”內保存的重定位表的表項大小為sizeof(Elf32_rel)= 8字節。
ELF符號表(Symbol Table**)
ELF文件的符號表保存了程序中的符號信息,包括程序中的文件名、函數名、全局變量名等,符號表一般保存在名為“.strtab”的段內,該段對應段表項的類型為SHT_SYMTAB。符號表包含多個符號表項,每個符號表項記錄了符號的名稱、位置、類型等信息。符號表象的數據結構:
| 1 2 3 4 5 6 7 8 9 | typedef?struct { ??Elf32_Word?????????? st_name;????????????????/* Symbol name (string tbl index) */ ??Elf32_Addr?????????? st_value;???????????????/* Symbol value */ ??Elf32_Word?????????? st_size;????????????????/* Symbol size */ ??unsigned?char????????st_info;????????????????/* Symbol type and binding */ ??unsigned?char????????st_other;???????????????/* Symbol visibility */ ??Elf32_Section??????? st_shndx;???????????????/* Section index */ } Elf32_Sym; |
?
?
ELF重定位表(Reloc Table)
重定位表常見于可重定位目標文件內,對于靜態鏈接生成的可可執行文件,一般不包括重定位表,動態鏈接生成的可執行文件暫時不討論。重定位表一般保存在以名為“.rel”開頭的段內,該段對應段表項的類型為SHT_REL,ELF文件需要重定位的段,一般都對應一個重定位表,比如代碼段“.txt”的重定位表保持在“.rel.text”內,數據段“.data”的重定位表保持在“.rel.data”內。
重定位表包含多個重定位表項,每個重定位表項記錄一條重定位信息,包括重定位的符號、位置、類型等。
/* Relocation table entry without addend (in section of type SHT_REL). */typedef struct {Elf32_Addr r_offset; /* Address */Elf32_Word r_info; /* Relocation type and symbol index */ } Elf32_Rel;?
ELF串表(String Table)
ELF文件內的段表和符號表需要記錄段名和符號名,這些名稱都是字符串。然而,段表項和符號表項都是固定長度的數據結構,無法存儲不定長的字符串。因此FLE文件將名稱字符串內容集中存放在一個段內,稱為串表。這些段表項和符號表項只需記錄段名字符串或符號名字符串在對應串表項的位置即可。
雖然雖然存儲的字符串表的內容稱為串表,但是并非表的形式,而是一個文件區域。
總結
以上是生活随笔為你收集整理的Linux之ELF文件初探 ---(elf源码)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 自定义图片形状
- 下一篇: linux 其他常用命令